Multiple devices in a single Usb HID descriptor - what am I missing?

I'm currently working on a project to get a Leonardo to report the state of multiple Joysticks to Windows. (This is to allow me to exceed the paltry 32 button limit per stick which windows imposes on a sim-pit controller.)

I'm modifying a HID descriptor which works to define a mouse, keyboard and joystick. However, every time I add additional joystick reports to the descriptor, windows starts giving me the amazingly generic and unhelpful "error 10" (device can not start.) This is the error given by a malformed HID descriptor, but I've tried many things, including an extremely simplified descriptor that I'm pretty certain is okay, and I still get the error.

Here's my current descriptor:

const u8 _hidReportDescriptor[] = {

#if KBAM_ENABLED
	//	Mouse
	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)	// 54
	0x09, 0x02,			// USAGE (Mouse)
	0xa1, 0x01,			// COLLECTION (Application)
	0x09, 0x01,			//   USAGE (Pointer)
	0xa1, 0x00,			//   COLLECTION (Physical)
	0x85, 0x01,			//     REPORT_ID (1)
	0x05, 0x09,			//     USAGE_PAGE (Button)
	0x19, 0x01,			//     USAGE_MINIMUM (Button 1)
	0x29, 0x03,			//     USAGE_MAXIMUM (Button 3)
	0x15, 0x00,			//     LOGICAL_MINIMUM (0)
	0x25, 0x01,			//     LOGICAL_MAXIMUM (1)
	0x95, 0x03,			//     REPORT_COUNT (3)
	0x75, 0x01,			//     REPORT_SIZE (1)
	0x81, 0x02,			//     INPUT (Data,Var,Abs)
	0x95, 0x01,			//     REPORT_COUNT (1)
	0x75, 0x05,			//     REPORT_SIZE (5)
	0x81, 0x03,			//     INPUT (Cnst,Var,Abs)
	0x05, 0x01,			//     USAGE_PAGE (Generic Desktop)
	0x09, 0x30,			//     USAGE (X)
	0x09, 0x31,			//     USAGE (Y)
	0x09, 0x38,			//     USAGE (Wheel)
	0x15, 0x81,			//     LOGICAL_MINIMUM (-127)
	0x25, 0x7f,			//     LOGICAL_MAXIMUM (127)
	0x75, 0x08,			//     REPORT_SIZE (8)
	0x95, 0x03,			//     REPORT_COUNT (3)
	0x81, 0x06,			//     INPUT (Data,Var,Rel)
	0xc0,			      //   END_COLLECTION
	0xc0,			      // END_COLLECTION

	//	Keyboard
	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)	// 47
	0x09, 0x06,			// USAGE (Keyboard)
	0xa1, 0x01,			// COLLECTION (Application)
	0x85, 0x02,			//   REPORT_ID (2)
	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, 0x01,			//   REPORT_COUNT (1)
	0x75, 0x08,			//   REPORT_SIZE (8)
	0x81, 0x03,			//   INPUT (Cnst,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
#endif


#ifdef JOYHID_ENABLED

	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)
	0x09, 0x05,			// USAGE (Joystick)
	0xa1, 0x01,			// COLLECTION (Application)
		0xA1, 0x00,			// COLLECTION (Physical)
			0x85, 0x03,			// REPORT_ID (3)
			//Buttons:
			0x05, 0x09,			// USAGE_PAGE (Button)
			0x19, 0x01,			// USAGE_MINIMUM (Button 1)
			0x29, 0x20,			// USAGE_MAXIMUM (Button 32)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x01,			// LOGICAL_MAXIMUM (1)
			0x75, 0x01,			// REPORT_SIZE (1)
			0x95, 0x20,			// REPORT_COUNT (32)
			0x55, 0x00,			// UNIT_EXPONENT (0)
			0x65, 0x00,			// UNIT (None)
			0x81, 0x02,			// INPUT (Data,Var,Abs)

			// 8 bit Throttle and Steering
			0x05, 0x02,			// USAGE_PAGE (Simulation Controls)

			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x26, 0xff, 0x00,		// LOGICAL_MAXIMUM (255)
			0xA1, 0x00,			// COLLECTION (Physical)
				0x09, 0xBB,			// USAGE (Throttle)
				0x09, 0xBA,			// USAGE (Steering)
				0x75, 0x08,			// REPORT_SIZE (8)
				0x95, 0x02,			// REPORT_COUNT (2)
				0x81, 0x02,			// INPUT (Data,Var,Abs)
			0xc0,				// END_COLLECTION
			// Two Hat switches

			0x05, 0x01,			// USAGE_PAGE (Generic Desktop)

			0x09, 0x39,			// USAGE (Hat switch)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x07,			// LOGICAL_MAXIMUM (7)
			0x35, 0x00,			// PHYSICAL_MINIMUM (0)
			0x46, 0x3B, 0x01,		// PHYSICAL_MAXIMUM (315)
			0x65, 0x14,			// UNIT (Eng Rot:Angular Pos)
			0x75, 0x04,			// REPORT_SIZE (4)
			0x95, 0x01,			// REPORT_COUNT (1)
			0x81, 0x02,			// INPUT (Data,Var,Abs)

			0x09, 0x39,			// USAGE (Hat switch)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x07,			// LOGICAL_MAXIMUM (7)
			0x35, 0x00,			// PHYSICAL_MINIMUM (0)
			0x46, 0x3B, 0x01,		// PHYSICAL_MAXIMUM (315)
			0x65, 0x14,			// UNIT (Eng Rot:Angular Pos)
			0x75, 0x04,			// REPORT_SIZE (4)
			0x95, 0x01,			// REPORT_COUNT (1)
			0x81, 0x02,			// INPUT (Data,Var,Abs)

			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x26, 0xff, 0x00,		// LOGICAL_MAXIMUM (255)
			0x75, 0x08,			// REPORT_SIZE (8)

			0x09, 0x01,			// USAGE (Pointer)
			0xA1, 0x00,			// COLLECTION (Physical)
				0x09, 0x30,		// USAGE (x)
				0x09, 0x31,		// USAGE (y)
				0x09, 0x32,		// USAGE (z)
				0x09, 0x33,		// USAGE (rx)
				0x09, 0x34,		// USAGE (ry)
				0x09, 0x35,		// USAGE (rz)
				0x95, 0x06,		// REPORT_COUNT (2)
				0x81, 0x02,		// INPUT (Data,Var,Abs)
			0xc0,				// END_COLLECTION
		0xc0,					// END_COLLECTION
	0xc0,				// END_COLLECTION

	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)
	0x09, 0x05,			// USAGE (Joystick)
	0xa1, 0x01,			// COLLECTION (Application)
		0xA1, 0x00,			// COLLECTION (Physical)
			0x85, 0x04,			// REPORT_ID (4)  (This is important when HID_SendReport() is called)

			//Buttons:
			0x05, 0x09,			// USAGE_PAGE (Button)
			0x19, 0x01,			// USAGE_MINIMUM (Button 1)
			0x29, 0x20,			// USAGE_MAXIMUM (Button 32)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x01,			// LOGICAL_MAXIMUM (1)
			0x75, 0x01,			// REPORT_SIZE (1)
			0x95, 0x20,			// REPORT_COUNT (32)
			0x55, 0x00,			// UNIT_EXPONENT (0)
			0x65, 0x00,			// UNIT (None)
			0x81, 0x02,			// INPUT (Data,Var,Abs)

			// 8 bit Throttle and Steering
			0x05, 0x02,			// USAGE_PAGE (Simulation Controls)

			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x26, 0xff, 0x00,		// LOGICAL_MAXIMUM (255)
			0xA1, 0x00,			// COLLECTION (Physical)
				0x09, 0xBB,			// USAGE (Throttle)
				0x09, 0xBA,			// USAGE (Steering)
				0x75, 0x08,			// REPORT_SIZE (8)
				0x95, 0x02,			// REPORT_COUNT (2)
				0x81, 0x02,			// INPUT (Data,Var,Abs)
			0xc0,				// END_COLLECTION
			// Two Hat switches

			0x05, 0x01,			// USAGE_PAGE (Generic Desktop)

			0x09, 0x39,			// USAGE (Hat switch)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x07,			// LOGICAL_MAXIMUM (7)
			0x35, 0x00,			// PHYSICAL_MINIMUM (0)
			0x46, 0x3B, 0x01,		// PHYSICAL_MAXIMUM (315)
			0x65, 0x14,			// UNIT (Eng Rot:Angular Pos)
			0x75, 0x04,			// REPORT_SIZE (4)
			0x95, 0x01,			// REPORT_COUNT (1)
			0x81, 0x02,			// INPUT (Data,Var,Abs)

			0x09, 0x39,			// USAGE (Hat switch)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x07,			// LOGICAL_MAXIMUM (7)
			0x35, 0x00,			// PHYSICAL_MINIMUM (0)
			0x46, 0x3B, 0x01,		// PHYSICAL_MAXIMUM (315)
			0x65, 0x14,			// UNIT (Eng Rot:Angular Pos)
			0x75, 0x04,			// REPORT_SIZE (4)
			0x95, 0x01,			// REPORT_COUNT (1)
			0x81, 0x02,			// INPUT (Data,Var,Abs)

			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x26, 0xff, 0x00,		// LOGICAL_MAXIMUM (255)
			0x75, 0x08,			// REPORT_SIZE (8)

			0x09, 0x01,			// USAGE (Pointer)
			0xA1, 0x00,			// COLLECTION (Physical)
				0x09, 0x30,		// USAGE (x)
				0x09, 0x31,		// USAGE (y)
				0x09, 0x32,		// USAGE (z)
				0x09, 0x33,		// USAGE (rx)
				0x09, 0x34,		// USAGE (ry)
				0x09, 0x35,		// USAGE (rz)
				0x95, 0x06,		// REPORT_COUNT (2)
				0x81, 0x02,		// INPUT (Data,Var,Abs)
			0xc0,				// END_COLLECTION
		0xc0,				// END_COLLECTION
	0xc0					// END_COLLECTION

#endif
};

I've tried removing the keyboard/mouse reports (I don't need them but would have preferred to keep them as this is in a core class for the Arduino.) I've tried defining the report ID number at both the application and physical collection. I've read examples which are logically identical (only big defference is they are defining gamepads instead of joysticks).

I'm assuming I'm missing something somewhere else which is required to make my expanded descriptor compatible with the device (Like a size definition or something - this is my first time working with USB HIDs).

Anyone have any idea what's going on?

My next step is to tear everything down and see if I can get just a single button on 2 separate joysticks working, but I'm not hopeful.

So, this descriptor works:

const u8 _hidReportDescriptor[] = {
// 32 buttons (and a throttle - just in case the game doesn't recognise a joystick with no analog axis)

 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
 0x09, 0x04, // USAGE (Joystick)
 0xa1, 0x01, // COLLECTION (Application)
 0x85, 0x01, // REPORT_ID (1)  (This is important when HID_SendReport() is called)
 //Buttons:
 0x05, 0x09, // USAGE_PAGE (Button)
 0x19, 0x01, // USAGE_MINIMUM (Button 1)
 0x29, 0x20, // USAGE_MAXIMUM (Button 32)
 0x15, 0x00, // LOGICAL_MINIMUM (0)
 0x25, 0x01, // LOGICAL_MAXIMUM (1)
 0x75, 0x01, // REPORT_SIZE (1)
 0x95, 0x20, // REPORT_COUNT (32)
 0x55, 0x00, // UNIT_EXPONENT (0)
 0x65, 0x00, // UNIT (None)
 0x81, 0x02, // INPUT (Data,Var,Abs)
 0xc0 // END_COLLECTION (Application)
};

But this one doesn't:

const u8 _hidReportDescriptor[] = {
// 32 buttons (and a throttle - just in case the game doesn't recognise a joystick with no analog axis)

 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
 0x09, 0x04, // USAGE (Joystick)
 0xa1, 0x01, // COLLECTION (Application)
 0x85, 0x01, // REPORT_ID (1)  (This is important when HID_SendReport() is called)
 //Buttons:
 0x05, 0x09, // USAGE_PAGE (Button)
 0x19, 0x01, // USAGE_MINIMUM (Button 1)
 0x29, 0x20, // USAGE_MAXIMUM (Button 32)
 0x15, 0x00, // LOGICAL_MINIMUM (0)
 0x25, 0x01, // LOGICAL_MAXIMUM (1)
 0x75, 0x01, // REPORT_SIZE (1)
 0x95, 0x20, // REPORT_COUNT (32)
 0x55, 0x00, // UNIT_EXPONENT (0)
 0x65, 0x00, // UNIT (None)
 0x81, 0x02, // INPUT (Data,Var,Abs)
 0xc0, // END_COLLECTION (Application)

        0x05, 0x01, // USAGE_PAGE (Generic Desktop)
 0x09, 0x04, // USAGE (Joystick)
 0xa1, 0x01, // COLLECTION (Application)
 0x85, 0x02, // REPORT_ID (2)  (This is important when HID_SendReport() is called)
 //Buttons:
 0x05, 0x09, // USAGE_PAGE (Button)
 0x19, 0x01, // USAGE_MINIMUM (Button 1)
 0x29, 0x20, // USAGE_MAXIMUM (Button 32)
 0x15, 0x00, // LOGICAL_MINIMUM (0)
 0x25, 0x01, // LOGICAL_MAXIMUM (1)
 0x75, 0x01, // REPORT_SIZE (1)
 0x95, 0x20, // REPORT_COUNT (32)
 0x55, 0x00, // UNIT_EXPONENT (0)
 0x65, 0x00, // UNIT (None)
 0x81, 0x02, // INPUT (Data,Var,Abs)
 0xc0 // END_COLLECTION (Application)
};

Despite the fact that the second one is just the first one twice with another report ID.

Ooookay...
So, after doing the ol' "binary search by commenting stuff out" I've narrowed the problem down to windows rejecting the descriptor any time I've defined more than one application collection.

This descriptor works, but Windows sees it as a single report with 64 buttons defined (only 32 of which can be read because of the game controller button limit). If you un-comment the bit in the middle, back to old error 10.

const u8 _hidReportDescriptor[] = {
// 32 buttons (and a throttle - just in case the game doesn't recognise a joystick with no analog axis)

	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)
	0x09, 0x04,			// USAGE (Joystick)
	0xa1, 0x01,			// COLLECTION (Application)
		0x85, 0x01,			// REPORT_ID (1)
		0xa1, 0x00,			// COLLECTION (Physical)
			//Buttons:
			0x05, 0x09,			// USAGE_PAGE (Button)
			0x19, 0x01,			// USAGE_MINIMUM (Button 1)
			0x29, 0x20,			// USAGE_MAXIMUM (Button 32)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x01,			// LOGICAL_MAXIMUM (1)
			0x75, 0x01,			// REPORT_SIZE (1)
			0x95, 0x20,			// REPORT_COUNT (32)
			0x55, 0x00,			// UNIT_EXPONENT (0)
			0x65, 0x00,			// UNIT (None)
			0x81, 0x02,			// INPUT (Data,Var,Abs)
		0xc0,					// END_COLLECTION (Physical)

	/*
	0xc0,					// END_COLLECTION (Application)
	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)
	0x09, 0x04,			// USAGE (Joystick)
	0xa1, 0x01,			// COLLECTION (Application)
	*/

		0x85, 0x02,			// REPORT_ID (2)
		0xa1, 0x00,			// COLLECTION (Physical)
			//Buttons:
			0x05, 0x09,			// USAGE_PAGE (Button)
			0x19, 0x01,			// USAGE_MINIMUM (Button 1)
			0x29, 0x20,			// USAGE_MAXIMUM (Button 32)
			0x15, 0x00,			// LOGICAL_MINIMUM (0)
			0x25, 0x01,			// LOGICAL_MAXIMUM (1)
			0x75, 0x01,			// REPORT_SIZE (1)
			0x95, 0x20,			// REPORT_COUNT (32)
			0x55, 0x00,			// UNIT_EXPONENT (0)
			0x65, 0x00,			// UNIT (None)
			0x81, 0x02,			// INPUT (Data,Var,Abs)
		0xc0,					// END_COLLECTION (Physical)
	0xc0					// END_COLLECTION (Application)

};

The thing is, I've had other descriptors with multiple application collections defined (I started basing it off one which had a mouse, keyboard, and joystick all defined in separate application collections) so I don't understand why it hates mine in particular. I thought maybe having two application collections with the same Usage might be what was confusing it, but setting one of the Joysticks to be a Gamepad doesn't help.