1//
2//  IOHIDReportDescriptorParser.c
3//  IOHIDFamily
4//
5//  Created by Rob Yepez on 2/23/13.
6//
7//
8
9#include <string.h>
10#include <limits.h>
11#include <stdarg.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15
16#include <sys/types.h>
17#include <sys/sysctl.h>
18#include <IOKit/hid/IOHIDUsageTables.h>
19#include "IOHIDReportDescriptorParser.h"
20
21#define    UnpackReportSize(packedByte)    ((packedByte) & 0x03)
22#define    UnpackReportType(packedByte)    (((packedByte) & 0x0C) >> 2)
23#define    UnpackReportTag(packedByte)    (((packedByte) & 0xF0) >> 4)
24
25enum
26{
27    kReport_TypeMain            = 0,
28    kReport_TypeGlobal            = 1,
29    kReport_TypeLocal            = 2,
30    kReport_TypeReserved        = 3,
31
32    kReport_TagLongItem            = 0x0F,
33
34    // main items
35    kReport_TagInput            = 0x08,
36    kReport_TagOutput            = 0x09,
37    kReport_TagFeature            = 0x0B,
38    kReport_TagCollection        = 0x0A,
39    kReport_TagEndCollection    = 0x0C,
40
41    // global items
42    kReport_TagUsagePage        = 0x00,
43    kReport_TagLogicalMin        = 0x01,
44    kReport_TagLogicalMax        = 0x02,
45    kReport_TagPhysicalMin        = 0x03,
46    kReport_TagPhysicalMax        = 0x04,
47    kReport_TagUnitExponent        = 0x05,
48    kReport_TagUnit                = 0x06,
49    kReport_TagReportSize        = 0x07,
50    kReport_TagReportID            = 0x08,
51    kReport_TagReportCount        = 0x09,
52    kReport_TagPush                = 0x0A,
53    kReport_TagPop                = 0x0B,
54
55    // local items
56    kReport_TagUsage            = 0x00,
57    kReport_TagUsageMin            = 0x01,
58    kReport_TagUsageMax            = 0x02,
59    kReport_TagDesignatorIndex    = 0x03,
60    kReport_TagDesignatorMin    = 0x04,
61    kReport_TagDesignatorMax    = 0x05,
62    kReport_TagStringIndex        = 0x07,
63    kReport_TagStringMin        = 0x08,
64    kReport_TagStringMax        = 0x09,
65    kReport_TagSetDelimiter        = 0x0A
66};
67
68// Collection constants
69enum
70{
71    kCollection_Physical        = 0x00,
72    kCollection_Application        = 0x01,
73    kCollection_Logical            = 0x02
74};
75
76// I/O constants (used for Input/Output/Feature tags)
77enum
78{
79    kIO_Data_or_Constant                = 0x0001,
80    kIO_Array_or_Variable                = 0x0002,
81    kIO_Absolute_or_Relative            = 0x0004,
82    kIO_NoWrap_or_Wrap                    = 0x0008,
83    kIO_Linear_or_NonLinear                = 0x0010,
84    kIO_PreferredState_or_NoPreferred    = 0x0020,
85    kIO_NoNullPosition_or_NullState        = 0x0040,
86    kIO_NonVolatile_or_Volatile            = 0x0080,        // reserved for Input
87    kIO_BitField_or_BufferedBytes        = 0x0100
88};
89
90
91static void PrintAtIndentLevel(unsigned int level, const char * format, ...)
92{
93    va_list     ap;
94
95    for (unsigned int i = 0; i < level; i++) {
96        fputs("    ", stdout);
97    }
98
99    va_start(ap, format);
100    vprintf(format, ap);
101    va_end(ap);
102}
103
104static void PrintBytesAtIndentLevel(unsigned int level, const uint8_t * data, uint32_t length)
105{
106    uint32_t index;
107
108    for ( index=0; index<length; index+=16 ) {
109        uint32_t innerIndex;
110        uint32_t innerLength = length-index;
111        if ( innerLength > 16 )
112            innerLength = 16;
113
114        PrintAtIndentLevel(level, "%08X: ", index);
115        for ( innerIndex=0; innerIndex<innerLength; innerIndex++)
116            printf("%02X ", data[index+innerIndex]);
117
118        printf("\n");
119    }
120}
121
122void PrintHIDDescriptor(const uint8_t *reportDesc, uint32_t length)
123{
124    const uint8_t *         end = reportDesc + length;
125    uint8_t                 size, type, tag;
126    uint32_t                usagePage = 0;
127    uint32_t                value=0;
128    int32_t                 svalue=0;
129    static unsigned char    buf[350], tempbuf[350], bufvalue[350], tempbufvalue[350];
130    int                     i, indentLevel;
131    int                     datahandled=0;
132    int                     itemsigned=0;
133    int                     len;
134
135    printf("\n");
136    printf("Raw HID Descriptor:\n");
137    printf("---------------------------------------------------------\n");
138
139    PrintBytesAtIndentLevel(0, reportDesc, length);
140
141    printf("\n");
142    printf("Parsed HID Descriptor:\n");
143    printf("---------------------------------------------------------\n");
144    indentLevel = 0;
145    while (reportDesc < end)
146    {
147        int padLevel = 7;
148
149        buf[0] = 0;
150        bufvalue[0] = 0;
151        size = UnpackReportSize(*reportDesc);
152        if (size == 3) size = 4;    // 0 == 0 bytes, 1 == 1 bytes, 2 == 2 bytes, but 3 == 4 bytes
153
154        type = UnpackReportType(*reportDesc);
155        tag = UnpackReportTag(*reportDesc);
156
157        sprintf((char *)tempbuf, "0x%02X, ", *(reportDesc++));
158        strcat((char *)buf, (char *)tempbuf);
159        padLevel--;
160
161        if (tag == kReport_TagLongItem)
162        {
163            size = *reportDesc;
164            sprintf((char *)tempbuf, "0x%02X, ", *(reportDesc++));
165            strcat((char *)buf, (char *)tempbuf);
166            tag = *reportDesc;
167            sprintf((char *)tempbuf, "0x%02X, ", *(reportDesc++));
168            strcat((char *)buf, (char *)tempbuf);
169
170            padLevel -= 2;
171        }
172
173
174        // if we're small enough, load the value into a register (byte swaping)
175        if (size <= 4)
176        {
177            value = 0;
178            for (i = 0; i < size; i++) {
179                value += (*(reportDesc)) << (i * 8);
180                sprintf((char *)tempbuf, "0x%02X, ", *(reportDesc++));
181                strcat((char *)buf, (char *)tempbuf);
182                padLevel--;
183            }
184
185            svalue = 0;
186            switch (size)
187            {
188                case 1: svalue = (int8_t) value; break;
189                case 2: svalue = (int16_t) value; break;
190
191                    // if the top bit is set, then sign extend it and fall thru to 32bit case
192                case 3: if (value & 0x00800000) value |= 0xFF000000; // no break
193                case 4: svalue = (int32_t) value; break;
194            }
195        }
196
197        // pad it a bit
198        for (i = 0; i < padLevel; i++)
199            strcat((char *)buf, "      ");
200
201        strcat((char *)buf, "// ");
202
203        // indent this line
204        for (i = 0; i < indentLevel; i++)
205            strcat((char *)buf, "  ");
206
207
208        // get the name of this tag, and do any specific data handling
209        datahandled = 0;
210        switch (type)
211        {
212            case kReport_TypeMain:
213                switch (tag)
214            {
215                case kReport_TagInput:
216                case kReport_TagOutput:
217                case kReport_TagFeature:
218                    switch (tag)
219                {
220                    case kReport_TagInput: strcat((char *)buf, "Input..................."); break;
221                    case kReport_TagOutput: strcat((char *)buf, "Output.................."); break;
222                    case kReport_TagFeature: strcat((char *)buf, "Feature................."); break;
223                }
224
225                    strcat((char *)bufvalue, (char *)"(");
226
227                    strcat((char *)bufvalue, (value & kIO_Data_or_Constant) ? "Constant, " : "Data, ");
228
229                    if ( (value & kIO_Data_or_Constant) == 0 ) {
230
231                        strcat((char *)bufvalue, (value & kIO_Array_or_Variable) ? "Variable, ": "Array, ");
232
233                        strcat((char *)bufvalue, (value & kIO_Absolute_or_Relative) ? "Relative, " : "Absolute, ");
234
235                        if (((tag == kReport_TagInput) && (value & kIO_Array_or_Variable)) || tag != kReport_TagInput)
236                        {
237    #if VERBOSE
238                            // these are only valid for variable inputs, and feature/output tags
239                            strcat((char *)bufvalue, (value & kIO_NoWrap_or_Wrap) ? "Wrap, " : "No Wrap, ");
240                            strcat((char *)bufvalue, (value & kIO_Linear_or_NonLinear) ? "Nonlinear, " : "Linear, ");
241                            strcat((char *)bufvalue, (value & kIO_PreferredState_or_NoPreferred) ? "No Preferred, " : "Preferred State, ");
242                            strcat((char *)bufvalue, (value & kIO_NoNullPosition_or_NullState) ? "Null State, " : "No Null Position, ");
243
244                            if (tag != kReport_TagInput)
245                                strcat((char *)bufvalue, (value & kIO_NonVolatile_or_Volatile) ? "Volatile, " : "Nonvolatile, ");
246                            strcat((char *)bufvalue, (value & kIO_BitField_or_BufferedBytes) ? "Buffered bytes" : "Bitfield");
247    #else
248                            // these are only valid for variable inputs, and feature/output tags
249                            strcat((char *)bufvalue, (value & kIO_NoWrap_or_Wrap) ? "Wrap, " : "");
250                            strcat((char *)bufvalue, (value & kIO_Linear_or_NonLinear) ? "Nonlinear, " : "");
251                            strcat((char *)bufvalue, (value & kIO_PreferredState_or_NoPreferred) ? "No Preferred, " : "");
252                            strcat((char *)bufvalue, (value & kIO_NoNullPosition_or_NullState) ? "Null State, " : "");
253
254                            if (tag != kReport_TagInput)
255                                strcat((char *)bufvalue, (value & kIO_NonVolatile_or_Volatile) ? "Volatile, " : "");
256                            strcat((char *)bufvalue, (value & kIO_BitField_or_BufferedBytes) ? "Buffered bytes" : "");
257    #endif
258                        }
259                    }
260
261                    len = strlen((char *)bufvalue);
262                    if ( strcmp((const char *)&bufvalue[len-2], ", ") == 0 )
263                        bufvalue[len-2]=0;
264                    strcat((char *)bufvalue, (char *)")");
265
266                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
267                    tempbufvalue[0] = 0;
268                    datahandled = 1;
269                    break;
270
271
272                case kReport_TagCollection:
273                    indentLevel++;
274
275                    sprintf((char *)tempbuf, "Collection ");
276
277                    strcat((char *)buf, (char *)tempbuf);
278
279                    strcat((char *)buf, (char *)"(");
280                    switch (value)
281                {
282                    case kCollection_Physical: sprintf((char *)tempbuf, "Physical"); break;
283                    case kCollection_Application:  sprintf((char *)tempbuf, "Application"); break;
284                    case kCollection_Logical: sprintf((char *)tempbuf, "Logical"); break;
285                }
286                    strcat((char *)buf, (char *)tempbuf);
287                    strcat((char *)buf, (char *)")");
288
289                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
290                    tempbufvalue[0] = 0;
291                    datahandled = 1;
292                    break;
293
294                case kReport_TagEndCollection:
295                {
296                    // recalc indentation, since we want this line to start earlier
297
298                    len = strlen((char *)buf);
299
300                    if (indentLevel-- && len > 2) {
301                        buf[len-2]=0;
302                    }
303
304                    sprintf((char *)tempbuf, "End Collection ");
305
306                }
307                    break;
308            }
309                break;
310
311            case kReport_TypeGlobal:
312                switch (tag)
313            {
314                case kReport_TagUsagePage:
315                    strcat((char *)buf, "Usage Page ");
316                    usagePage = value;
317                    strcat((char *)bufvalue, (char *)"(");
318                    switch (usagePage)
319                    {
320                        case kHIDPage_GenericDesktop: sprintf((char *)tempbufvalue, "Generic Desktop"); break;
321                        case kHIDPage_Simulation: sprintf((char *)tempbufvalue, "Simulation Controls"); break;
322                        case kHIDPage_VR: sprintf((char *)tempbufvalue, "VR Controls"); break;
323                        case kHIDPage_Sport: sprintf((char *)tempbufvalue, "Sports Controls"); break;
324                        case kHIDPage_Game: sprintf((char *)tempbufvalue, "Game Controls"); break;
325                        case kHIDPage_KeyboardOrKeypad: sprintf((char *)tempbufvalue, "Keyboard/Keypad"); break;
326                        case kHIDPage_LEDs: sprintf((char *)tempbufvalue, "LED"); break;
327                        case kHIDPage_Button: sprintf((char *)tempbufvalue, "Button"); break;
328                        case kHIDPage_Ordinal: sprintf((char *)tempbufvalue, "Ordinal"); break;
329                        case kHIDPage_Telephony: sprintf((char *)tempbufvalue, "Telephony Device"); break;
330                        case kHIDPage_Consumer: sprintf((char *)tempbufvalue, "Consumer"); break;
331                        case kHIDPage_Digitizer: sprintf((char *)tempbufvalue, "Digitizer"); break;
332                        case kHIDPage_PID: sprintf((char *)tempbufvalue, "PID"); break;
333                        case kHIDPage_Unicode: sprintf((char *)tempbufvalue, "Unicode"); break;
334                        case kHIDPage_AlphanumericDisplay: sprintf((char *)tempbufvalue, "Alphanumeric Display"); break;
335                        case kHIDPage_Monitor: sprintf((char *)tempbufvalue, "Monitor"); break;
336                        case kHIDPage_MonitorEnumerated: sprintf((char *)tempbufvalue, "Monitor Enumerated Values"); break;
337                        case kHIDPage_MonitorVirtual: sprintf((char *)tempbufvalue, "VESA Virtual Controls"); break;
338                        case kHIDPage_MonitorReserved: sprintf((char *)tempbufvalue, "Monitor Class reserved"); break;
339                        case kHIDPage_PowerDevice: sprintf((char *)tempbufvalue, "Power Device"); break;
340                        case kHIDPage_BatterySystem: sprintf((char *)tempbufvalue, "Battery System"); break;
341                        case kHIDPage_PowerReserved: sprintf((char *)tempbufvalue, "Power Class reserved"); break;
342                        case kHIDPage_PowerReserved2: sprintf((char *)tempbufvalue, "Power Class reserved"); break;
343                        case 0xff: sprintf((char *)tempbufvalue, "Vendor Defined"); break;
344
345                        default: sprintf((char *)tempbufvalue, "%u", usagePage); break;
346                    }
347
348                    //strcat((char *)buf, (char *)tempbuf);
349                    strcat((char *)bufvalue, (char *)tempbufvalue);
350                    strcat((char *)bufvalue, (char *)")");
351                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
352                    tempbufvalue[0] = 0;
353                    datahandled = 1;
354                    break;
355
356                case kReport_TagLogicalMin: sprintf((char *)tempbuf,      "Logical Minimum......... "); itemsigned=1; break;
357                case kReport_TagLogicalMax: sprintf((char *)tempbuf,      "Logical Maximum......... "); itemsigned=1; break;
358                case kReport_TagPhysicalMin: sprintf((char *)tempbuf,     "Physical Minimum........ "); itemsigned=1; break;
359                case kReport_TagPhysicalMax: sprintf((char *)tempbuf,     "Physical Maximum........ "); itemsigned=1; break;
360                case kReport_TagUnitExponent: sprintf((char *)tempbuf,    "Unit Exponent........... "); break;
361                case kReport_TagUnit: sprintf((char *)tempbuf,            "Unit.................... "); break;
362                case kReport_TagReportSize: sprintf((char *)tempbuf,      "Report Size............. "); break;
363                case kReport_TagReportID: sprintf((char *)tempbuf,        "ReportID................ "); break;
364                case kReport_TagReportCount: sprintf((char *)tempbuf,     "Report Count............ "); break;
365                case kReport_TagPush: sprintf((char *)tempbuf,            "Push.................... "); break;
366                case kReport_TagPop: sprintf((char *)tempbuf,             "Pop..................... "); break;
367            }
368                break;
369
370            case kReport_TypeLocal:
371                switch (tag)
372            {
373                case kReport_TagUsage:
374                    sprintf((char *)tempbuf, "Usage ");
375                    strcat((char *)buf, (char *)tempbuf);
376                    if (usagePage == kHIDPage_GenericDesktop)
377                    {
378                        strcat((char *)buf, (char *)"(");
379                        switch (value)
380                        {
381                            case kHIDUsage_GD_Pointer: sprintf((char *)tempbuf, "Pointer"); break;
382                            case kHIDUsage_GD_Mouse: sprintf((char *)tempbuf, "Mouse"); break;
383                            case kHIDUsage_GD_Joystick: sprintf((char *)tempbuf, "Joystick"); break;
384                            case kHIDUsage_GD_GamePad: sprintf((char *)tempbuf, "GamePad"); break;
385                            case kHIDUsage_GD_Keyboard: sprintf((char *)tempbuf, "Keyboard"); break;
386                            case kHIDUsage_GD_Keypad: sprintf((char *)tempbuf, "Keypad"); break;
387                            case kHIDUsage_GD_MultiAxisController:  sprintf((char *)tempbuf, "MultiAxisController"); break;
388
389                            case kHIDUsage_GD_X: sprintf((char *)tempbuf, "X"); break;
390                            case kHIDUsage_GD_Y: sprintf((char *)tempbuf, "Y"); break;
391                            case kHIDUsage_GD_Z: sprintf((char *)tempbuf, "Z"); break;
392                            case kHIDUsage_GD_Rx: sprintf((char *)tempbuf, "Rx"); break;
393                            case kHIDUsage_GD_Ry: sprintf((char *)tempbuf, "Ry"); break;
394                            case kHIDUsage_GD_Rz: sprintf((char *)tempbuf, "Rz"); break;
395                            case kHIDUsage_GD_Slider: sprintf((char *)tempbuf, "Slider"); break;
396                            case kHIDUsage_GD_Dial: sprintf((char *)tempbuf, "Dial"); break;
397                            case kHIDUsage_GD_Wheel: sprintf((char *)tempbuf, "Wheel"); break;
398                            case kHIDUsage_GD_Hatswitch: sprintf((char *)tempbuf, "Hat Switch"); break;
399                            case kHIDUsage_GD_CountedBuffer: sprintf((char *)tempbuf, "Counted Buffer"); break;
400                            case kHIDUsage_GD_ByteCount: sprintf((char *)tempbuf, "Byte Count"); break;
401                            case kHIDUsage_GD_MotionWakeup: sprintf((char *)tempbuf, "Motion Wakeup"); break;
402
403                            case kHIDUsage_GD_Vx: sprintf((char *)tempbuf, "Vx"); break;
404                            case kHIDUsage_GD_Vy: sprintf((char *)tempbuf, "Vy"); break;
405                            case kHIDUsage_GD_Vz: sprintf((char *)tempbuf, "Vz"); break;
406                            case kHIDUsage_GD_Vbrx: sprintf((char *)tempbuf, "Vbrx"); break;
407                            case kHIDUsage_GD_Vbry: sprintf((char *)tempbuf, "Vbry"); break;
408                            case kHIDUsage_GD_Vbrz: sprintf((char *)tempbuf, "Vbrz"); break;
409                            case kHIDUsage_GD_Vno: sprintf((char *)tempbuf, "Vno"); break;
410
411                            case kHIDUsage_GD_SystemControl: sprintf((char *)tempbuf, "System Control"); break;
412                            case kHIDUsage_GD_SystemPowerDown: sprintf((char *)tempbuf, "System Power Down"); break;
413                            case kHIDUsage_GD_SystemSleep: sprintf((char *)tempbuf, "System Sleep"); break;
414                            case kHIDUsage_GD_SystemWakeUp: sprintf((char *)tempbuf, "System Wakeup"); break;
415                            case kHIDUsage_GD_SystemContextMenu: sprintf((char *)tempbuf, "System Context Menu"); break;
416                            case kHIDUsage_GD_SystemMainMenu: sprintf((char *)tempbuf, "System Main Menu"); break;
417                            case kHIDUsage_GD_SystemAppMenu: sprintf((char *)tempbuf, "System App Menu"); break;
418                            case kHIDUsage_GD_SystemMenuHelp: sprintf((char *)tempbuf, "System Menu Help"); break;
419                            case kHIDUsage_GD_SystemMenuExit: sprintf((char *)tempbuf, "System Menu Exit"); break;
420                            case kHIDUsage_GD_SystemMenuSelect: sprintf((char *)tempbuf, "System Menu Select"); break;
421                            case kHIDUsage_GD_SystemMenuRight: sprintf((char *)tempbuf, "System Menu Right"); break;
422                            case kHIDUsage_GD_SystemMenuLeft: sprintf((char *)tempbuf, "System Menu Left"); break;
423                            case kHIDUsage_GD_SystemMenuUp: sprintf((char *)tempbuf, "System Menu Up"); break;
424                            case kHIDUsage_GD_SystemMenuDown: sprintf((char *)tempbuf, "System Menu Down"); break;
425
426                            default: sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value); break;
427                        }
428                        strcat((char *)tempbuf, (char *)")");
429                    }
430                    else if (usagePage == kHIDPage_Digitizer)
431                    {
432                        strcat((char *)buf, (char *)"(");
433                        switch (value)
434                        {
435                            case kHIDUsage_Dig_Digitizer: sprintf((char *)tempbuf, "Digitizer"); break;
436                            case kHIDUsage_Dig_Pen: sprintf((char *)tempbuf, "Pen"); break;
437                            case kHIDUsage_Dig_LightPen: sprintf((char *)tempbuf, "Light Pen"); break;
438                            case kHIDUsage_Dig_TouchScreen: sprintf((char *)tempbuf, "Touch Screen"); break;
439                            case kHIDUsage_Dig_TouchPad: sprintf((char *)tempbuf, "Touch Pad"); break;
440                            case kHIDUsage_Dig_WhiteBoard: sprintf((char *)tempbuf, "White Board"); break;
441                            case kHIDUsage_Dig_CoordinateMeasuringMachine: sprintf((char *)tempbuf, "Coordinate Measuring Machine"); break;
442                            case kHIDUsage_Dig_3DDigitizer: sprintf((char *)tempbuf, "3D Digitizer"); break;
443                            case kHIDUsage_Dig_StereoPlotter: sprintf((char *)tempbuf, "Stereo Plotter"); break;
444                            case kHIDUsage_Dig_ArticulatedArm: sprintf((char *)tempbuf, "Articulated Arm"); break;
445                            case kHIDUsage_Dig_Armature: sprintf((char *)tempbuf, "Armature"); break;
446                            case kHIDUsage_Dig_MultiplePointDigitizer: sprintf((char *)tempbuf, "Multi Point Digitizer"); break;
447                            case kHIDUsage_Dig_FreeSpaceWand: sprintf((char *)tempbuf, "Free Space Wand"); break;
448                            case kHIDUsage_Dig_DeviceConfiguration: sprintf((char *)tempbuf, "Device Configuration"); break;
449                            case kHIDUsage_Dig_Stylus: sprintf((char *)tempbuf, "Stylus"); break;
450                            case kHIDUsage_Dig_Puck: sprintf((char *)tempbuf, "Puck"); break;
451                            case kHIDUsage_Dig_Finger: sprintf((char *)tempbuf, "Finger"); break;
452                            case kHIDUsage_Dig_DeviceSettings: sprintf((char *)tempbuf, "Device Settings"); break;
453                            case kHIDUsage_Dig_GestureCharacter: sprintf((char *)tempbuf, "Gesture Character"); break;
454                            case kHIDUsage_Dig_TipPressure: sprintf((char *)tempbuf, "Tip Pressure"); break;
455                            case kHIDUsage_Dig_BarrelPressure: sprintf((char *)tempbuf, "Barrel Pressure"); break;
456                            case kHIDUsage_Dig_InRange: sprintf((char *)tempbuf, "In Range"); break;
457                            case kHIDUsage_Dig_Touch: sprintf((char *)tempbuf, "Touch"); break;
458                            case kHIDUsage_Dig_Untouch: sprintf((char *)tempbuf, "Untouch"); break;
459                            case kHIDUsage_Dig_Tap: sprintf((char *)tempbuf, "Tap"); break;
460                            case kHIDUsage_Dig_Quality: sprintf((char *)tempbuf, "Quality"); break;
461                            case kHIDUsage_Dig_DataValid: sprintf((char *)tempbuf, "Data Valid"); break;
462                            case kHIDUsage_Dig_TransducerIndex: sprintf((char *)tempbuf, "Transducer Index"); break;
463                            case kHIDUsage_Dig_TabletFunctionKeys: sprintf((char *)tempbuf, "Tablet Function Keys"); break;
464                            case kHIDUsage_Dig_ProgramChangeKeys: sprintf((char *)tempbuf, "Program Change Buttons"); break;
465                            case kHIDUsage_Dig_BatteryStrength: sprintf((char *)tempbuf, "Battery Strength"); break;
466                            case kHIDUsage_Dig_Invert: sprintf((char *)tempbuf, "Invert"); break;
467                            case kHIDUsage_Dig_XTilt: sprintf((char *)tempbuf, "X Tilt"); break;
468                            case kHIDUsage_Dig_YTilt: sprintf((char *)tempbuf, "Y Tilt"); break;
469                            case kHIDUsage_Dig_Azimuth: sprintf((char *)tempbuf, "Azimuth"); break;
470                            case kHIDUsage_Dig_Altitude: sprintf((char *)tempbuf, "Altitude"); break;
471                            case kHIDUsage_Dig_Twist: sprintf((char *)tempbuf, "Twist"); break;
472                            case kHIDUsage_Dig_TipSwitch: sprintf((char *)tempbuf, "Tip Switch"); break;
473                            case kHIDUsage_Dig_SecondaryTipSwitch: sprintf((char *)tempbuf, "Secondary Tip Switch"); break;
474                            case kHIDUsage_Dig_BarrelSwitch: sprintf((char *)tempbuf, "Barrel Switch"); break;
475                            case kHIDUsage_Dig_Eraser: sprintf((char *)tempbuf, "Eraser"); break;
476                            case kHIDUsage_Dig_TabletPick: sprintf((char *)tempbuf, "Tablet Pick"); break;
477                            case kHIDUsage_Dig_TouchValid: sprintf((char *)tempbuf, "Touch Valid"); break;
478                            case kHIDUsage_Dig_Width: sprintf((char *)tempbuf, "Width"); break;
479                            case kHIDUsage_Dig_Height: sprintf((char *)tempbuf, "Height"); break;
480                            case kHIDUsage_Dig_GestureCharacterEnable: sprintf((char *)tempbuf, "Gesture Character Enable"); break;
481                            case kHIDUsage_Dig_GestureCharacterQuality: sprintf((char *)tempbuf, "Gesture Character Quality"); break;
482                            case kHIDUsage_Dig_GestureCharacterDataLength: sprintf((char *)tempbuf, "Gesture Character Data Length"); break;
483                            case kHIDUsage_Dig_GestureCharacterData: sprintf((char *)tempbuf, "Gesture Character Data"); break;
484                            case kHIDUsage_Dig_GestureCharacterEncoding: sprintf((char *)tempbuf, "Gesture Character Encoding"); break;
485                            case kHIDUsage_Dig_GestureCharacterEncodingUTF8: sprintf((char *)tempbuf, "Gesture Character Encoding UTF8"); break;
486                            case kHIDUsage_Dig_GestureCharacterEncodingUTF16LE: sprintf((char *)tempbuf, "Gesture Character Encoding UTF16 Little Endian"); break;
487                            case kHIDUsage_Dig_GestureCharacterEncodingUTF16BE: sprintf((char *)tempbuf, "Gesture Character Encoding UTF16 Big Endian"); break;
488                            case kHIDUsage_Dig_GestureCharacterEncodingUTF32LE: sprintf((char *)tempbuf, "Gesture Character Encoding UTF32 Little Endian"); break;
489                            case kHIDUsage_Dig_GestureCharacterEncodingUTF32BE: sprintf((char *)tempbuf, "Gesture Character Encoding UTF32 Big Endian"); break;
490
491                            default: sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value); break;
492                        }
493                        strcat((char *)tempbuf, (char *)")");
494                    }
495                    else if (usagePage == kHIDPage_PID)
496                    {
497                        strcat((char *)buf, (char *)"(");
498                        switch (value)
499                        {
500                            case 1: sprintf((char *)tempbuf, "Physical Interface Device"); break;
501                            case 0x20: sprintf((char *)tempbuf, "Normal"); break;
502                            case 0x21: sprintf((char *)tempbuf, "Set Effect Report"); break;
503                            case 0x22: sprintf((char *)tempbuf, "Effect Block Index"); break;
504                            case 0x23: sprintf((char *)tempbuf, "Parameter Block Offset"); break;
505                            case 0x24: sprintf((char *)tempbuf, "ROM Flag"); break;
506                            case 0x25: sprintf((char *)tempbuf, "Effect Type"); break;
507                            case 0x26: sprintf((char *)tempbuf, "ET Constant Force"); break;
508                            case 0x27: sprintf((char *)tempbuf, "ET Ramp"); break;
509                            case 0x28: sprintf((char *)tempbuf, "ET Custom Force Data"); break;
510                            case 0x30: sprintf((char *)tempbuf, "ET Square"); break;
511                            case 0x31: sprintf((char *)tempbuf, "ET Sine"); break;
512                            case 0x32: sprintf((char *)tempbuf, "ET Triangle"); break;
513                            case 0x33: sprintf((char *)tempbuf, "ET Sawtooth Up"); break;
514                            case 0x34: sprintf((char *)tempbuf, "ET Sawtooth Down"); break;
515                            case 0x40: sprintf((char *)tempbuf, "ET Spring"); break;
516                            case 0x41: sprintf((char *)tempbuf, "ET Damper"); break;
517                            case 0x42: sprintf((char *)tempbuf, "ET Inertia"); break;
518                            case 0x43: sprintf((char *)tempbuf, "ET Friction"); break;
519                            case 0x50: sprintf((char *)tempbuf, "Duration"); break;
520                            case 0x51: sprintf((char *)tempbuf, "Sample Period"); break;
521                            case 0x52: sprintf((char *)tempbuf, "Gain"); break;
522                            case 0x53: sprintf((char *)tempbuf, "Trigger Button"); break;
523                            case 0x54: sprintf((char *)tempbuf, "Trigger Repeat Interval"); break;
524                            case 0x55: sprintf((char *)tempbuf, "Axes Enable"); break;
525                            case 0x56: sprintf((char *)tempbuf, "Direction Enable"); break;
526                            case 0x57: sprintf((char *)tempbuf, "Direction"); break;
527                            case 0x58: sprintf((char *)tempbuf, "Type Specific Block Offset"); break;
528                            case 0x59: sprintf((char *)tempbuf, "Block Type"); break;
529                            case 0x5a: sprintf((char *)tempbuf, "Set Envelope Report"); break;
530                            case 0x5b: sprintf((char *)tempbuf, "Attack Level"); break;
531                            case 0x5c: sprintf((char *)tempbuf, "Attack Time"); break;
532                            case 0x5d: sprintf((char *)tempbuf, "Fade Level"); break;
533                            case 0x5e: sprintf((char *)tempbuf, "Fade Time"); break;
534                            case 0x5f: sprintf((char *)tempbuf, "Set Condition Report"); break;
535                            case 0x60: sprintf((char *)tempbuf, "CP Offset"); break;
536                            case 0x61: sprintf((char *)tempbuf, "Positive Coefficient"); break;
537                            case 0x62: sprintf((char *)tempbuf, "Negative Coefficient"); break;
538                            case 0x63: sprintf((char *)tempbuf, "Positive Saturation"); break;
539                            case 0x64: sprintf((char *)tempbuf, "Negative Saturation"); break;
540                            case 0x65: sprintf((char *)tempbuf, "Dead Band"); break;
541                            case 0x66: sprintf((char *)tempbuf, "Download Force Data Report"); break;
542                            case 0x67: sprintf((char *)tempbuf, "Isoch Custom Force Enable"); break;
543                            case 0x68: sprintf((char *)tempbuf, "Custom Force Data Report"); break;
544                            case 0x69: sprintf((char *)tempbuf, "Custom Force Data"); break;
545                            case 0x6a: sprintf((char *)tempbuf, "Custom Force Vendor Defined Data"); break;
546                            case 0x6b: sprintf((char *)tempbuf, "Set Custom Force Report"); break;
547                            case 0x6c: sprintf((char *)tempbuf, "Custom Force Data Offset"); break;
548                            case 0x6d: sprintf((char *)tempbuf, "Sample Count"); break;
549                            case 0x6e: sprintf((char *)tempbuf, "Set Periodic Report"); break;
550                            case 0x6f: sprintf((char *)tempbuf, "Offset"); break;
551                            case 0x70: sprintf((char *)tempbuf, "Magnitude"); break;
552                            case 0x71: sprintf((char *)tempbuf, "Phase"); break;
553                            case 0x72: sprintf((char *)tempbuf, "Period"); break;
554                            case 0x73: sprintf((char *)tempbuf, "Set Constant Force Report"); break;
555                            case 0x74: sprintf((char *)tempbuf, "Set Constant Force"); break;
556                            case 0x75: sprintf((char *)tempbuf, "Ramp Start"); break;
557                            case 0x76: sprintf((char *)tempbuf, "Ramp End"); break;
558                            case 0x77: sprintf((char *)tempbuf, "Effect Operation Report"); break;
559                            case 0x78: sprintf((char *)tempbuf, "Effect Operation"); break;
560                            case 0x79: sprintf((char *)tempbuf, "Op Effect Start"); break;
561                            case 0x7a: sprintf((char *)tempbuf, "Op Effect Start Solo"); break;
562                            case 0x7b: sprintf((char *)tempbuf, "Op Effect Stop"); break;
563                            case 0x7c: sprintf((char *)tempbuf, "Loop Count"); break;
564                            case 0x7d: sprintf((char *)tempbuf, "Gain Report"); break;
565                            case 0x7e: sprintf((char *)tempbuf, "Gain"); break;
566                            case 0x7f: sprintf((char *)tempbuf, "PID Pool Report"); break;
567                            case 0x80: sprintf((char *)tempbuf, "RAM Pool Size"); break;
568                            case 0x81: sprintf((char *)tempbuf, "ROM Pool Size"); break;
569                            case 0x82: sprintf((char *)tempbuf, "ROM Effect Block Count"); break;
570                            case 0x83: sprintf((char *)tempbuf, "Simultaneous Effects Max"); break;
571                            case 0x84: sprintf((char *)tempbuf, "Pool Alignment"); break;
572                            case 0x85: sprintf((char *)tempbuf, "PID Pool Move Report"); break;
573                            case 0x86: sprintf((char *)tempbuf, "Move Source"); break;
574                            case 0x87: sprintf((char *)tempbuf, "Move Destination"); break;
575                            case 0x88: sprintf((char *)tempbuf, "Move Length"); break;
576                            case 0x89: sprintf((char *)tempbuf, "PID Block Load Report"); break;
577                            case 0x8b: sprintf((char *)tempbuf, "Block Load Status"); break;
578                            case 0x8c: sprintf((char *)tempbuf, "Block Load Success"); break;
579                            case 0x8d: sprintf((char *)tempbuf, "Block Load Full"); break;
580                            case 0x8e: sprintf((char *)tempbuf, "Block Load Error"); break;
581                            case 0x8f: sprintf((char *)tempbuf, "Block Handle"); break;
582                            case 0x90: sprintf((char *)tempbuf, "PID Block Free Report"); break;
583                            case 0x91: sprintf((char *)tempbuf, "Type Specific Block Handle"); break;
584                            case 0x92: sprintf((char *)tempbuf, "PID State Report"); break;
585                            case 0x94: sprintf((char *)tempbuf, "Effect Playing"); break;
586                            case 0x95: sprintf((char *)tempbuf, "PID Device Control Report"); break;
587                            case 0x96: sprintf((char *)tempbuf, "PID Device Control"); break;
588                            case 0x97: sprintf((char *)tempbuf, "DC Enable Actuators"); break;
589                            case 0x98: sprintf((char *)tempbuf, "DC Disable Actuators"); break;
590                            case 0x99: sprintf((char *)tempbuf, "DC Stoop All Effects"); break;
591                            case 0x9a: sprintf((char *)tempbuf, "DC Device Reset"); break;
592                            case 0x9b: sprintf((char *)tempbuf, "DC Device Pause"); break;
593                            case 0x9c: sprintf((char *)tempbuf, "DC Device Continue"); break;
594                            case 0x9f: sprintf((char *)tempbuf, "Device Paused"); break;
595                            case 0xa0: sprintf((char *)tempbuf, "Actuators Enabled"); break;
596                            case 0xa4: sprintf((char *)tempbuf, "Safety Switch"); break;
597                            case 0xa5: sprintf((char *)tempbuf, "Actuator Override Switch"); break;
598                            case 0xa6: sprintf((char *)tempbuf, "Actuator Power"); break;
599                            case 0xa7: sprintf((char *)tempbuf, "Start Delay"); break;
600                            case 0xa8: sprintf((char *)tempbuf, "Parameter Block Size"); break;
601                            case 0xa9: sprintf((char *)tempbuf, "Device Managed Pool"); break;
602                            case 0xaa: sprintf((char *)tempbuf, "Shared parameter blocks"); break;
603                            case 0xab: sprintf((char *)tempbuf, "Create New Effect Report"); break;
604                            case 0xac: sprintf((char *)tempbuf, "RAM Pool Available"); break;
605
606
607                            default: sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value); break;
608                        }
609                        strcat((char *)tempbuf, (char *)")");
610                    }
611                    else
612                    {
613                        sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value);
614                    }
615
616                    strcat((char *)buf, (char *)tempbuf);
617                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
618                    tempbufvalue[0] = 0;
619                    datahandled = 1;
620                    break;
621
622                case kReport_TagUsageMin: sprintf((char *)tempbuf,        "Usage Minimum........... "); break;
623                case kReport_TagUsageMax: sprintf((char *)tempbuf,        "Usage Maximum........... "); break;
624                case kReport_TagDesignatorIndex: sprintf((char *)tempbuf, "Designator Index........ "); break;
625                case kReport_TagDesignatorMin: sprintf((char *)tempbuf,   "Designator Minumum...... "); break;
626                case kReport_TagDesignatorMax: sprintf((char *)tempbuf,   "Designator Maximum...... "); break;
627                case kReport_TagStringIndex: sprintf((char *)tempbuf,     "String Index............ "); break;
628                case kReport_TagStringMin: sprintf((char *)tempbuf,       "String Minimum.......... "); break;
629                case kReport_TagStringMax: sprintf((char *)tempbuf,       "String Maximum.......... "); break;
630                case kReport_TagSetDelimiter: sprintf((char *)tempbuf,    "Set Delimiter........... "); break;
631            }
632                break;
633
634            case kReport_TypeReserved:
635                sprintf((char *)tempbuf, "Reserved "); break;
636                break;
637        }
638
639        // actually put in the data from the switch -- why not just strcat there??
640        strcat((char *)buf, (char *)tempbuf);
641
642        // if we didn't handle the data before, print in generic fashion
643        if (!datahandled )
644        {
645            if (size) {
646                strcat((char *)bufvalue, (char *)"(");
647                if (size <= 4)
648                {
649                    if (itemsigned)
650                    {
651                        sprintf((char *)tempbufvalue, "%d", (int32_t)svalue);
652                    }
653                    else
654                    {
655                        sprintf((char *)tempbufvalue, "%u", (uint32_t)value);
656                    }
657                    strcat((char *)bufvalue, (char *)tempbufvalue);
658                }
659                else
660                    for (i = 0; i < size; i++)
661                    {
662                        sprintf((char *)tempbufvalue, "%02X ", *(reportDesc++));
663                        strcat((char *)bufvalue, (char *)tempbufvalue);
664                    }
665                strcat((char *)bufvalue, (char *)") ");
666            }
667            itemsigned=0;
668        }
669
670
671        // finally add the info
672        strcat((char *)bufvalue, " "); // in case bufvalue was empty, add a blank space
673
674
675        // this juggling is because the End Collection tags were not nested deep enough in the OutlineView.
676
677        /*        if (tag == kReport_TagEndCollection) {
678         [self PrintKeyVal:buf val:bufvalue forDevice:deviceNumber atDepth:depth+indentLevel+1 forNode:node];
679         }
680         else
681         [self PrintKeyVal:buf val:bufvalue forDevice:deviceNumber atDepth:depth+indentLevel forNode:node];*/
682
683        printf("%s%s\n",buf, bufvalue);
684    }
685}
686