+
+ LEAVE('-');
+}
+
+/**
+ * \brief Handle a Collection item in a report
+ *
+ * When an application collection is found in the root of the tree, the approriate
+ * sub-handler is invoked (mouse, keyboard, gamepad, ...)
+ */
+tHID_ReportCallbacks *HID_RootCollection(
+ tUSBInterface *Dev,
+ tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
+ )
+{
+ Uint32 usage;
+
+ // Check for "Application" collection
+ if( Value != 1 ) return NULL;
+
+ // Check usages
+ if( Local->Usages.nItems == 0 ) return NULL;
+ usage = Local->Usages.Items[0];
+ switch(usage >> 16)
+ {
+ case 0x0001: // General Desktop
+ switch(usage & 0xFFFF)
+ {
+ case 0x0001: // Pointer
+ LOG("Desktop->Pointer");
+ break;
+ case 0x0002: // Mouse
+ LOG("Desktop->Mouse");
+ return &gHID_Mouse_ReportCBs;
+ case 0x0004: // Joystick
+ case 0x0005: // Game Pad
+ LOG("Desktop->Gamepad");
+ break;
+ case 0x0006: // Keyboard
+ LOG("Desktop->Keyboard");
+ return &gHID_Kb_ReportCBs;
+ }
+ break;
+ case 0x0007: // Keyboard / Keypad
+ LOG("Keyboard");
+ return &gHID_Kb_ReportCBs;
+ }
+ return NULL;
+}
+
+void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
+{
+ const int max_cb_level = 8;
+ int cb_level = 0;
+ tHID_ReportCallbacks *cur_cbs;
+ tHID_ReportCallbacks *cb_stack[max_cb_level];
+ tHID_ReportGlobalState global_state;
+ tHID_ReportLocalState local_state;
+
+ ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
+
+ // Initialise callback stack
+ cb_stack[0] = StartCBs;
+ cur_cbs = StartCBs;
+
+ // Clear state
+ memset(&global_state, 0, sizeof(global_state));
+ memset(&local_state, 0, sizeof(local_state));
+
+ // Iterate though the report data
+ for( int ofs = 0; ofs < Length; )
+ {
+ Uint8 byte;
+ Uint32 val;
+
+ // --- Get value and length ---
+ byte = Data[ofs];
+ switch(byte & 3)
+ {
+ case 0:
+ val = 0;
+ ofs += 1;
+ break;
+ case 1:
+ if( ofs + 2 > Length ) { LEAVE('-'); return; }
+ val = Data[ofs+1];
+ ofs += 2;
+ break;
+ case 2:
+ if( ofs + 3 > Length ) { LEAVE('-'); return; }
+ val = Data[ofs + 1] | (Data[ofs + 1]<<8);
+ ofs += 3;
+ break;
+ case 3:
+ if( ofs + 5 > Length ) { LEAVE('-'); return; }
+ val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
+ ofs += 5;
+ break;
+ }
+
+ // --- Process the item ---
+ LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
+ switch(byte & 0xFC)
+ {
+ // Main items
+ // - Input
+ case 0x80:
+ LOG("Input 0x%x", val);
+ if( cur_cbs && cur_cbs->Input )
+ cur_cbs->Input( Dev, &global_state, &local_state, val );
+ goto _clear_local;
+ // - Output
+ case 0x90:
+ LOG("Output 0x%x", val);
+ if( cur_cbs && cur_cbs->Output )
+ cur_cbs->Output( Dev, &global_state, &local_state, val );
+ goto _clear_local;
+ // - Collection
+ case 0xA0:
+ LOG("Collection 0x%x", val);
+
+ tHID_ReportCallbacks *next_cbs = NULL;
+ if( cur_cbs && cur_cbs->Collection )
+ next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
+ cb_level ++;
+ if( cb_level < max_cb_level )
+ cb_stack[cb_level] = next_cbs;
+ else
+ next_cbs = NULL;
+ cur_cbs = next_cbs;
+ goto _clear_local;
+ // - Feature
+ case 0xB0:
+ LOG("Feature 0x%x", val);
+ if( cur_cbs && cur_cbs->Feature )
+ cur_cbs->Feature( Dev, &global_state, &local_state, val );
+ goto _clear_local;
+ // - End collection
+ case 0xC0:
+ if( cur_cbs && cur_cbs->EndCollection )
+ cur_cbs->EndCollection(Dev);
+ if( cb_level == 0 ) {
+ Log_Warning("USB_HID", "Inbalance in HID collections");
+ LEAVE('-');
+ return ;
+ }
+ cb_level --;
+ if( cb_level < max_cb_level )
+ cur_cbs = cb_stack[cb_level];
+ goto _clear_local;
+ // -- Helper to clear the local state
+ _clear_local:
+ _FreeList( &local_state.Strings );
+ _FreeList( &local_state.Usages );
+ _FreeList( &local_state.Designators );
+ memset( &local_state, 0, sizeof(local_state) );
+ break;
+
+ // Global Items
+ case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page
+ case 0x14: global_state.LogMin = val; break; // - Logical Min
+ case 0x24: global_state.LogMax = val; break; // - Logical Max
+ case 0x34: global_state.PhysMin = val; break; // - Physical Min
+ case 0x44: global_state.PhysMax = val; break; // - Physical Max
+ case 0x54: global_state.UnitExp = val; break; // - Unit Exp
+ case 0x64: global_state.Unit = val; break; // - Unit
+ case 0x74: global_state.ReportSize = val; break; // - Report Size
+ case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used)
+ case 0x94: global_state.ReportCount = val; break; // - Report Count
+ case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push
+ case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop
+
+ // Local Items
+ // - Usages
+ case 0x08: // Single
+ if( (byte & 3) != 3 ) val |= global_state.UsagePage;
+ LOG("Usage %x", val);
+ _AddItem(&local_state.Usages, val);
+ break;
+ case 0x18: // Range start
+ if( (byte & 3) != 3 ) val |= global_state.UsagePage;
+ LOG("Usage start %x", val);
+ local_state.UsageMin = val;
+ break;
+ case 0x28: // Range end (apply)
+ if( (byte & 3) != 3 ) val |= global_state.UsagePage;
+ LOG("Usage end %x (from %x)", val, local_state.UsageMin);
+ _AddItems(&local_state.Usages, local_state.UsageMin, val);
+ break;
+ // - Designators (Index into Physical report)
+ case 0x38:
+ _AddItem(&local_state.Designators, val);
+ break;
+ case 0x48:
+ local_state.DesignatorMin = val;
+ break;
+ case 0x58:
+ _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
+ break;
+ // - Reserved/hole
+ case 0x68:
+ break;
+ // - Strings
+ case 0x78:
+ _AddItem(&local_state.Strings, val);
+ break;
+ case 0x88:
+ local_state.StringMin = val;
+ break;
+ case 0x98:
+ _AddItems(&local_state.Strings, local_state.StringMin, val);
+ break;
+ // - Delimiter
+ case 0xA8:
+ break;
+
+ // Long Item
+ case 0xFC:
+ LOG("Long Item");
+ break;
+
+ // Reserved
+ default:
+ LOG("0x%x RESVD", byte & 0xFC);
+ break;
+ }
+ }
+
+ LEAVE('-');
+}
+
+// --------------------------------------------------------------------
+// List helpers
+// --------------------------------------------------------------------
+static void _AddItem(struct sHID_IntList *List, Uint32 Value)
+{
+ if( List->Space == List->nItems )
+ {
+ List->Space += 10;
+ List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
+ }
+
+// LOG("Added %x to %p", Value, List);
+ List->Items[ List->nItems ] = Value;
+ List->nItems ++;
+}
+
+static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
+{
+ while( First <= Last )
+ {
+ _AddItem(List, First);
+ First ++;
+ }
+}
+
+static void _FreeList(struct sHID_IntList *List)
+{
+ if( List->Items )
+ free(List->Items);