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 "IOHIDReportDescriptorParser.h"
11
12#define    UnpackReportSize(packedByte)    ((packedByte) & 0x03)
13#define    UnpackReportType(packedByte)    (((packedByte) & 0x0C) >> 2)
14#define    UnpackReportTag(packedByte)    (((packedByte) & 0xF0) >> 4)
15
16enum
17{
18    kReport_TypeMain            = 0,
19    kReport_TypeGlobal            = 1,
20    kReport_TypeLocal            = 2,
21    kReport_TypeReserved        = 3,
22
23    kReport_TagLongItem            = 0x0F,
24
25    // main items
26    kReport_TagInput            = 0x08,
27    kReport_TagOutput            = 0x09,
28    kReport_TagFeature            = 0x0B,
29    kReport_TagCollection        = 0x0A,
30    kReport_TagEndCollection    = 0x0C,
31
32    // global items
33    kReport_TagUsagePage        = 0x00,
34    kReport_TagLogicalMin        = 0x01,
35    kReport_TagLogicalMax        = 0x02,
36    kReport_TagPhysicalMin        = 0x03,
37    kReport_TagPhysicalMax        = 0x04,
38    kReport_TagUnitExponent        = 0x05,
39    kReport_TagUnit                = 0x06,
40    kReport_TagReportSize        = 0x07,
41    kReport_TagReportID            = 0x08,
42    kReport_TagReportCount        = 0x09,
43    kReport_TagPush                = 0x0A,
44    kReport_TagPop                = 0x0B,
45
46    // local items
47    kReport_TagUsage            = 0x00,
48    kReport_TagUsageMin            = 0x01,
49    kReport_TagUsageMax            = 0x02,
50    kReport_TagDesignatorIndex    = 0x03,
51    kReport_TagDesignatorMin    = 0x04,
52    kReport_TagDesignatorMax    = 0x05,
53    kReport_TagStringIndex        = 0x07,
54    kReport_TagStringMin        = 0x08,
55    kReport_TagStringMax        = 0x09,
56    kReport_TagSetDelimiter        = 0x0A
57};
58
59// Collection constants
60enum
61{
62    kCollection_Physical        = 0x00,
63    kCollection_Application        = 0x01,
64    kCollection_Logical            = 0x02
65};
66
67// I/O constants (used for Input/Output/Feature tags)
68enum
69{
70    kIO_Data_or_Constant                = 0x0001,
71    kIO_Array_or_Variable                = 0x0002,
72    kIO_Absolute_or_Relative            = 0x0004,
73    kIO_NoWrap_or_Wrap                    = 0x0008,
74    kIO_Linear_or_NonLinear                = 0x0010,
75    kIO_PreferredState_or_NoPreferred    = 0x0020,
76    kIO_NoNullPosition_or_NullState        = 0x0040,
77    kIO_NonVolatile_or_Volatile            = 0x0080,        // reserved for Input
78    kIO_BitField_or_BufferedBytes        = 0x0100
79};
80
81// Usage pages from HID Usage Tables spec 1.0
82enum
83{
84    kUsage_PageGenericDesktop            = 0x01,
85    kUsage_PageSimulationControls        = 0x02,
86    kUsage_PageVRControls                = 0x03,
87    kUsage_PageSportControls            = 0x04,
88    kUsage_PageGameControls                = 0x05,
89    kUsage_PageKeyboard                    = 0x07,
90    kUsage_PageLED                        = 0x08,
91    kUsage_PageButton                    = 0x09,
92    kUsage_PageOrdinal                    = 0x0A,
93    kUsage_PageTelephonyDevice            = 0x0B,
94    kUsage_PageConsumer                    = 0x0C,
95    kUsage_PageDigitizers                = 0x0D,
96    kUsage_PagePID                = 0x0F,
97    kUsage_PageUnicode                    = 0x10,
98    kUsage_PageAlphanumericDisplay        = 0x14,
99    kUsage_PageMonitor                    = 0x80,
100    kUsage_PageMonitorEnumeratedValues    = 0x81,
101    kUsage_PageMonitorVirtualControl     = 0x82,
102    kUsage_PageMonitorReserved            = 0x83,
103    kUsage_PagePowerDevice                = 0x84,
104    kUsage_PageBatterySystem            = 0x85,
105    kUsage_PowerClassReserved            = 0x86,
106    kUsage_PowerClassReserved2            = 0x87
107};
108
109// Usage constants for Generic Desktop page (01) from HID Usage Tables spec 1.0
110enum
111{
112    kUsage_01_Pointer        = 0x01,
113    kUsage_01_Mouse            = 0x02,
114    kUsage_01_Joystick        = 0x04,
115    kUsage_01_GamePad        = 0x05,
116    kUsage_01_Keyboard        = 0x06,
117    kUsage_01_Keypad        = 0x07,
118
119    kUsage_01_X                = 0x30,
120    kUsage_01_Y                = 0x31,
121    kUsage_01_Z                = 0x32,
122    kUsage_01_Rx            = 0x33,
123    kUsage_01_Ry            = 0x34,
124    kUsage_01_Rz            = 0x35,
125    kUsage_01_Slider        = 0x36,
126    kUsage_01_Dial            = 0x37,
127    kUsage_01_Wheel            = 0x38,
128    kUsage_01_HatSwitch        = 0x39,
129    kUsage_01_CountedBuffer    = 0x3A,
130    kUsage_01_ByteCount        = 0x3B,
131    kUsage_01_MotionWakeup    = 0x3C,
132
133    kUsage_01_Vx            = 0x40,
134    kUsage_01_Vy            = 0x41,
135    kUsage_01_Vz            = 0x42,
136    kUsage_01_Vbrx            = 0x43,
137    kUsage_01_Vbry            = 0x44,
138    kUsage_01_Vbrz            = 0x45,
139    kUsage_01_Vno            = 0x46,
140
141    kUsage_01_SystemControl        = 0x80,
142    kUsage_01_SystemPowerDown     = 0x81,
143    kUsage_01_SystemSleep         = 0x82,
144    kUsage_01_SystemWakeup        = 0x83,
145    kUsage_01_SystemContextMenu = 0x84,
146    kUsage_01_SystemMainMenu    = 0x85,
147    kUsage_01_SystemAppMenu        = 0x86,
148    kUsage_01_SystemMenuHelp    = 0x87,
149    kUsage_01_SystemMenuExit    = 0x88,
150    kUsage_01_SystemMenuSelect    = 0x89,
151    kUsage_01_SystemMenuRight    = 0x8A,
152    kUsage_01_SystemMenuLeft    = 0x8B,
153    kUsage_01_SystemMenuUp        = 0x8C,
154    kUsage_01_SystemMenuDown    = 0x8D
155};
156
157void PrintHIDReport(uint8_t *reportDesc, uint32_t length)
158{
159    uint8_t *               end = reportDesc + length;
160    uint8_t                 size, type, tag;
161    uint32_t                usagePage = 0;
162    uint32_t                value=0;
163    int32_t                 svalue=0;
164    static unsigned char    buf[350], tempbuf[350], bufvalue[350], tempbufvalue[350];
165    int                     i, indentLevel;
166    int                     datahandled=0;
167    int                     usagesigned=0;
168
169
170    indentLevel = 1;
171
172    while (reportDesc < end)
173    {
174        size = UnpackReportSize(*reportDesc);
175        if (size == 3) size = 4;    // 0 == 0 bytes, 1 == 1 bytes, 2 == 2 bytes, but 3 == 4 bytes
176
177        type = UnpackReportType(*reportDesc);
178        tag = UnpackReportTag(*reportDesc);
179        reportDesc++;
180
181        if (tag == kReport_TagLongItem)
182        {
183            size = *reportDesc++;
184            tag = *reportDesc++;
185        }
186
187
188        // if we're small enough, load the value into a register (byte swaping)
189        if (size <= 4)
190        {
191            value = 0;
192            for (i = 0; i < size; i++)
193                value += (*(reportDesc++)) << (i * 8);
194
195            svalue = 0;
196            switch (size)
197            {
198                case 1: svalue = (int8_t) value; break;
199                case 2: svalue = (int16_t) value; break;
200
201                    // if the top bit is set, then sign extend it and fall thru to 32bit case
202                case 3: if (value & 0x00800000) value |= 0xFF000000; // no break
203                case 4: svalue = (int32_t) value; break;
204            }
205        }
206
207        // indent this line
208        buf[0] = 0;
209        bufvalue[0] = 0;
210        for (i = 0; i < indentLevel; i++)
211            strcat((char *)buf, "  ");
212
213
214        // get the name of this tag, and do any specific data handling
215        datahandled = 0;
216        switch (type)
217        {
218            case kReport_TypeMain:
219                switch (tag)
220            {
221                case kReport_TagInput:
222                case kReport_TagOutput:
223                case kReport_TagFeature:
224                    switch (tag)
225                {
226                    case kReport_TagInput: strcat((char *)buf, "Input..................."); break;
227                    case kReport_TagOutput: strcat((char *)buf, "Output.................."); break;
228                    case kReport_TagFeature: strcat((char *)buf, "Feature................."); break;
229                }
230
231                    strcat((char *)bufvalue, (char *)"(");
232
233                    strcat((char *)bufvalue, (value & kIO_Data_or_Constant) ? "Constant, " : "Data, ");
234
235                    strcat((char *)bufvalue, (value & kIO_Array_or_Variable) ? "Variable, ": "Array, ");
236
237                    strcat((char *)bufvalue, (value & kIO_Absolute_or_Relative) ? "Relative" : "Absolute");
238
239                    if (((tag == kReport_TagInput) && (value & kIO_Array_or_Variable)) || tag != kReport_TagInput)
240                    {    // these are only valid for variable inputs, and feature/output tags
241                        strcat((char *)bufvalue, (value & kIO_NoWrap_or_Wrap) ? ", Wrap, " : ", No Wrap, ");
242
243                        strcat((char *)bufvalue, (value & kIO_Linear_or_NonLinear) ? "Nonlinear, " : "Linear, ");
244
245                        strcat((char *)bufvalue, (value & kIO_PreferredState_or_NoPreferred) ? "No Preferred, " : "Preferred State, ");
246
247                        strcat((char *)bufvalue, (value & kIO_NoNullPosition_or_NullState) ? "Null State, " : "No Null Position, ");
248
249                        if (tag != kReport_TagInput)
250                            strcat((char *)bufvalue, (value & kIO_NonVolatile_or_Volatile) ? "Volatile, " : "Nonvolatile, ");
251
252                        strcat((char *)bufvalue, (value & kIO_BitField_or_BufferedBytes) ? "Buffered bytes" : "Bitfield");
253                    }
254
255                    strcat((char *)bufvalue, (char *)")");
256
257                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
258                    tempbufvalue[0] = 0;
259                    datahandled = 1;
260                    break;
261
262
263                case kReport_TagCollection:
264                    indentLevel++;
265
266                    sprintf((char *)tempbuf, "Collection ");
267
268                    strcat((char *)buf, (char *)tempbuf);
269
270                    strcat((char *)buf, (char *)"(");
271                    switch (value)
272                {
273                    case kCollection_Physical: sprintf((char *)tempbuf, "Physical"); break;
274                    case kCollection_Application:  sprintf((char *)tempbuf, "Application"); break;
275                    case kCollection_Logical: sprintf((char *)tempbuf, "Logical"); break;
276                }
277                    strcat((char *)buf, (char *)tempbuf);
278                    strcat((char *)buf, (char *)")");
279
280                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
281                    tempbufvalue[0] = 0;
282                    datahandled = 1;
283                    break;
284
285                case kReport_TagEndCollection:
286                    // recalc indentation, since we want this line to start earlier
287                    indentLevel--;
288
289                    buf[0] = 0;
290                    for (i = 0; i < indentLevel; i++) {
291                        strcat((char *)buf, "  ");
292                    }
293
294                    sprintf((char *)tempbuf, "End Collection ");
295
296
297                    break;
298            }
299                break;
300
301            case kReport_TypeGlobal:
302                switch (tag)
303            {
304                case kReport_TagUsagePage:
305                    strcat((char *)buf, "Usage Page ");
306
307                    usagesigned = 1;
308                    usagePage = value;
309                    strcat((char *)bufvalue, (char *)"(");
310                    switch (usagePage)
311                {
312                    case kUsage_PageGenericDesktop: sprintf((char *)tempbufvalue, "Generic Desktop"); break;
313                    case kUsage_PageSimulationControls: sprintf((char *)tempbufvalue, "Simulation Controls"); break;
314                    case kUsage_PageVRControls: sprintf((char *)tempbufvalue, "VR Controls"); break;
315                    case kUsage_PageSportControls: sprintf((char *)tempbufvalue, "Sports Controls"); break;
316                    case kUsage_PageGameControls: sprintf((char *)tempbufvalue, "Game Controls"); break;
317                    case kUsage_PageKeyboard:
318                        sprintf((char *)tempbufvalue, "Keyboard/Keypad");
319                        usagesigned = 0;
320                        break;
321
322                    case kUsage_PageLED: sprintf((char *)tempbufvalue, "LED"); break;
323                    case kUsage_PageButton: sprintf((char *)tempbufvalue, "Button"); break;
324                    case kUsage_PageOrdinal: sprintf((char *)tempbufvalue, "Ordinal"); break;
325                    case kUsage_PageTelephonyDevice: sprintf((char *)tempbufvalue, "Telephony Device"); break;
326                    case kUsage_PageConsumer: sprintf((char *)tempbufvalue, "Consumer"); break;
327                    case kUsage_PageDigitizers: sprintf((char *)tempbufvalue, "Digitizer"); break;
328                    case kUsage_PagePID: sprintf((char *)tempbufvalue, "PID"); break;
329                    case kUsage_PageUnicode: sprintf((char *)tempbufvalue, "Unicode"); break;
330                    case kUsage_PageAlphanumericDisplay: sprintf((char *)tempbufvalue, "Alphanumeric Display"); break;
331                    case kUsage_PageMonitor: sprintf((char *)tempbufvalue, "Monitor"); break;
332                    case kUsage_PageMonitorEnumeratedValues: sprintf((char *)tempbufvalue, "Monitor Enumerated Values"); break;
333                    case kUsage_PageMonitorVirtualControl: sprintf((char *)tempbufvalue, "VESA Virtual Controls"); break;
334                    case kUsage_PageMonitorReserved: sprintf((char *)tempbufvalue, "Monitor Class reserved"); break;
335                    case kUsage_PagePowerDevice: sprintf((char *)tempbufvalue, "Power Device"); break;
336                    case kUsage_PageBatterySystem: sprintf((char *)tempbufvalue, "Battery System"); break;
337                    case kUsage_PowerClassReserved: sprintf((char *)tempbufvalue, "Power Class reserved"); break;
338                    case kUsage_PowerClassReserved2: sprintf((char *)tempbufvalue, "Power Class reserved"); break;
339                    case 0xff: sprintf((char *)tempbufvalue, "Vendor Defined"); break;
340
341                    default: sprintf((char *)tempbufvalue, "%u", usagePage); break;
342                }
343
344                    //strcat((char *)buf, (char *)tempbuf);
345                    strcat((char *)bufvalue, (char *)tempbufvalue);
346                    strcat((char *)bufvalue, (char *)")");
347                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
348                    tempbufvalue[0] = 0;
349                    datahandled = 1;
350                    break;
351
352                case kReport_TagLogicalMin: sprintf((char *)tempbuf,      "Logical Minimum......... "); break;
353                case kReport_TagLogicalMax: sprintf((char *)tempbuf,      "Logical Maximum......... "); break;
354                case kReport_TagPhysicalMin: sprintf((char *)tempbuf,     "Physical Minimum........ "); break;
355                case kReport_TagPhysicalMax: sprintf((char *)tempbuf,     "Physical Maximum........ "); break;
356                case kReport_TagUnitExponent: sprintf((char *)tempbuf,    "Unit Exponent........... "); break;
357                case kReport_TagUnit: sprintf((char *)tempbuf,            "Unit.................... "); break;
358                case kReport_TagReportSize: sprintf((char *)tempbuf,      "Report Size............. "); break;
359                case kReport_TagReportID: sprintf((char *)tempbuf,        "ReportID................ "); break;
360                case kReport_TagReportCount: sprintf((char *)tempbuf,     "Report Count............ "); break;
361                case kReport_TagPush: sprintf((char *)tempbuf,            "Push.................... "); break;
362                case kReport_TagPop: sprintf((char *)tempbuf,             "Pop..................... "); break;
363            }
364                break;
365
366            case kReport_TypeLocal:
367                switch (tag)
368            {
369                case kReport_TagUsage:
370                    sprintf((char *)tempbuf, "Usage ");
371                    strcat((char *)buf, (char *)tempbuf);
372                    if (usagePage == kUsage_PageGenericDesktop)
373                    {
374                        strcat((char *)buf, (char *)"(");
375                        switch (value)
376                        {
377                            case kUsage_01_Pointer: sprintf((char *)tempbuf, "Pointer"); break;
378                            case kUsage_01_Mouse: sprintf((char *)tempbuf, "Mouse"); break;
379                            case kUsage_01_Joystick: sprintf((char *)tempbuf, "Joystick"); break;
380                            case kUsage_01_GamePad: sprintf((char *)tempbuf, "GamePad"); break;
381                            case kUsage_01_Keyboard: sprintf((char *)tempbuf, "Keyboard"); break;
382                            case kUsage_01_Keypad: sprintf((char *)tempbuf, "Keypad"); break;
383
384                            case kUsage_01_X: sprintf((char *)tempbuf, "X"); break;
385                            case kUsage_01_Y: sprintf((char *)tempbuf, "Y"); break;
386                            case kUsage_01_Z: sprintf((char *)tempbuf, "Z"); break;
387                            case kUsage_01_Rx: sprintf((char *)tempbuf, "Rx"); break;
388                            case kUsage_01_Ry: sprintf((char *)tempbuf, "Ry"); break;
389                            case kUsage_01_Rz: sprintf((char *)tempbuf, "Rz"); break;
390                            case kUsage_01_Slider: sprintf((char *)tempbuf, "Slider"); break;
391                            case kUsage_01_Dial: sprintf((char *)tempbuf, "Dial"); break;
392                            case kUsage_01_Wheel: sprintf((char *)tempbuf, "Wheel"); break;
393                            case kUsage_01_HatSwitch: sprintf((char *)tempbuf, "Hat Switch"); break;
394                            case kUsage_01_CountedBuffer: sprintf((char *)tempbuf, "Counted Buffer"); break;
395                            case kUsage_01_ByteCount: sprintf((char *)tempbuf, "Byte Count"); break;
396                            case kUsage_01_MotionWakeup: sprintf((char *)tempbuf, "Motion Wakeup"); break;
397
398                            case kUsage_01_Vx: sprintf((char *)tempbuf, "Vx"); break;
399                            case kUsage_01_Vy: sprintf((char *)tempbuf, "Vy"); break;
400                            case kUsage_01_Vz: sprintf((char *)tempbuf, "Vz"); break;
401                            case kUsage_01_Vbrx: sprintf((char *)tempbuf, "Vbrx"); break;
402                            case kUsage_01_Vbry: sprintf((char *)tempbuf, "Vbry"); break;
403                            case kUsage_01_Vbrz: sprintf((char *)tempbuf, "Vbrz"); break;
404                            case kUsage_01_Vno: sprintf((char *)tempbuf, "Vno"); break;
405
406                            case kUsage_01_SystemControl: sprintf((char *)tempbuf, "System Control"); break;
407                            case kUsage_01_SystemPowerDown: sprintf((char *)tempbuf, "System Power Down"); break;
408                            case kUsage_01_SystemSleep: sprintf((char *)tempbuf, "System Sleep"); break;
409                            case kUsage_01_SystemWakeup: sprintf((char *)tempbuf, "System Wakeup"); break;
410                            case kUsage_01_SystemContextMenu: sprintf((char *)tempbuf, "System Context Menu"); break;
411                            case kUsage_01_SystemMainMenu: sprintf((char *)tempbuf, "System Main Menu"); break;
412                            case kUsage_01_SystemAppMenu: sprintf((char *)tempbuf, "System App Menu"); break;
413                            case kUsage_01_SystemMenuHelp: sprintf((char *)tempbuf, "System Menu Help"); break;
414                            case kUsage_01_SystemMenuExit: sprintf((char *)tempbuf, "System Menu Exit"); break;
415                            case kUsage_01_SystemMenuSelect: sprintf((char *)tempbuf, "System Menu Select"); break;
416                            case kUsage_01_SystemMenuRight: sprintf((char *)tempbuf, "System Menu Right"); break;
417                            case kUsage_01_SystemMenuLeft: sprintf((char *)tempbuf, "System Menu Left"); break;
418                            case kUsage_01_SystemMenuUp: sprintf((char *)tempbuf, "System Menu Up"); break;
419                            case kUsage_01_SystemMenuDown: sprintf((char *)tempbuf, "System Menu Down"); break;
420
421                            default: sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value); break;
422                        }
423                        strcat((char *)tempbuf, (char *)")");
424                    }
425                    else if (usagePage == kUsage_PagePID)
426                    {
427                        strcat((char *)buf, (char *)"(");
428                        switch (value)
429                        {
430                            case 1: sprintf((char *)tempbuf, "Physical Interface Device"); break;
431                            case 0x20: sprintf((char *)tempbuf, "Normal"); break;
432                            case 0x21: sprintf((char *)tempbuf, "Set Effect Report"); break;
433                            case 0x22: sprintf((char *)tempbuf, "Effect Block Index"); break;
434                            case 0x23: sprintf((char *)tempbuf, "Parameter Block Offset"); break;
435                            case 0x24: sprintf((char *)tempbuf, "ROM Flag"); break;
436                            case 0x25: sprintf((char *)tempbuf, "Effect Type"); break;
437                            case 0x26: sprintf((char *)tempbuf, "ET Constant Force"); break;
438                            case 0x27: sprintf((char *)tempbuf, "ET Ramp"); break;
439                            case 0x28: sprintf((char *)tempbuf, "ET Custom Force Data"); break;
440                            case 0x30: sprintf((char *)tempbuf, "ET Square"); break;
441                            case 0x31: sprintf((char *)tempbuf, "ET Sine"); break;
442                            case 0x32: sprintf((char *)tempbuf, "ET Triangle"); break;
443                            case 0x33: sprintf((char *)tempbuf, "ET Sawtooth Up"); break;
444                            case 0x34: sprintf((char *)tempbuf, "ET Sawtooth Down"); break;
445                            case 0x40: sprintf((char *)tempbuf, "ET Spring"); break;
446                            case 0x41: sprintf((char *)tempbuf, "ET Damper"); break;
447                            case 0x42: sprintf((char *)tempbuf, "ET Inertia"); break;
448                            case 0x43: sprintf((char *)tempbuf, "ET Friction"); break;
449                            case 0x50: sprintf((char *)tempbuf, "Duration"); break;
450                            case 0x51: sprintf((char *)tempbuf, "Sample Period"); break;
451                            case 0x52: sprintf((char *)tempbuf, "Gain"); break;
452                            case 0x53: sprintf((char *)tempbuf, "Trigger Button"); break;
453                            case 0x54: sprintf((char *)tempbuf, "Trigger Repeat Interval"); break;
454                            case 0x55: sprintf((char *)tempbuf, "Axes Enable"); break;
455                            case 0x56: sprintf((char *)tempbuf, "Direction Enable"); break;
456                            case 0x57: sprintf((char *)tempbuf, "Direction"); break;
457                            case 0x58: sprintf((char *)tempbuf, "Type Specific Block Offset"); break;
458                            case 0x59: sprintf((char *)tempbuf, "Block Type"); break;
459                            case 0x5a: sprintf((char *)tempbuf, "Set Envelope Report"); break;
460                            case 0x5b: sprintf((char *)tempbuf, "Attack Level"); break;
461                            case 0x5c: sprintf((char *)tempbuf, "Attack Time"); break;
462                            case 0x5d: sprintf((char *)tempbuf, "Fade Level"); break;
463                            case 0x5e: sprintf((char *)tempbuf, "Fade Time"); break;
464                            case 0x5f: sprintf((char *)tempbuf, "Set Condition Report"); break;
465                            case 0x60: sprintf((char *)tempbuf, "CP Offset"); break;
466                            case 0x61: sprintf((char *)tempbuf, "Positive Coefficient"); break;
467                            case 0x62: sprintf((char *)tempbuf, "Negative Coefficient"); break;
468                            case 0x63: sprintf((char *)tempbuf, "Positive Saturation"); break;
469                            case 0x64: sprintf((char *)tempbuf, "Negative Saturation"); break;
470                            case 0x65: sprintf((char *)tempbuf, "Dead Band"); break;
471                            case 0x66: sprintf((char *)tempbuf, "Download Force Data Report"); break;
472                            case 0x67: sprintf((char *)tempbuf, "Isoch Custom Force Enable"); break;
473                            case 0x68: sprintf((char *)tempbuf, "Custom Force Data Report"); break;
474                            case 0x69: sprintf((char *)tempbuf, "Custom Force Data"); break;
475                            case 0x6a: sprintf((char *)tempbuf, "Custom Force Vendor Defined Data"); break;
476                            case 0x6b: sprintf((char *)tempbuf, "Set Custom Force Report"); break;
477                            case 0x6c: sprintf((char *)tempbuf, "Custom Force Data Offset"); break;
478                            case 0x6d: sprintf((char *)tempbuf, "Sample Count"); break;
479                            case 0x6e: sprintf((char *)tempbuf, "Set Periodic Report"); break;
480                            case 0x6f: sprintf((char *)tempbuf, "Offset"); break;
481                            case 0x70: sprintf((char *)tempbuf, "Magnitude"); break;
482                            case 0x71: sprintf((char *)tempbuf, "Phase"); break;
483                            case 0x72: sprintf((char *)tempbuf, "Period"); break;
484                            case 0x73: sprintf((char *)tempbuf, "Set Constant Force Report"); break;
485                            case 0x74: sprintf((char *)tempbuf, "Set Constant Force"); break;
486                            case 0x75: sprintf((char *)tempbuf, "Ramp Start"); break;
487                            case 0x76: sprintf((char *)tempbuf, "Ramp End"); break;
488                            case 0x77: sprintf((char *)tempbuf, "Effect Operation Report"); break;
489                            case 0x78: sprintf((char *)tempbuf, "Effect Operation"); break;
490                            case 0x79: sprintf((char *)tempbuf, "Op Effect Start"); break;
491                            case 0x7a: sprintf((char *)tempbuf, "Op Effect Start Solo"); break;
492                            case 0x7b: sprintf((char *)tempbuf, "Op Effect Stop"); break;
493                            case 0x7c: sprintf((char *)tempbuf, "Loop Count"); break;
494                            case 0x7d: sprintf((char *)tempbuf, "Gain Report"); break;
495                            case 0x7e: sprintf((char *)tempbuf, "Gain"); break;
496                            case 0x7f: sprintf((char *)tempbuf, "PID Pool Report"); break;
497                            case 0x80: sprintf((char *)tempbuf, "RAM Pool Size"); break;
498                            case 0x81: sprintf((char *)tempbuf, "ROM Pool Size"); break;
499                            case 0x82: sprintf((char *)tempbuf, "ROM Effect Block Count"); break;
500                            case 0x83: sprintf((char *)tempbuf, "Simultaneous Effects Max"); break;
501                            case 0x84: sprintf((char *)tempbuf, "Pool Alignment"); break;
502                            case 0x85: sprintf((char *)tempbuf, "PID Pool Move Report"); break;
503                            case 0x86: sprintf((char *)tempbuf, "Move Source"); break;
504                            case 0x87: sprintf((char *)tempbuf, "Move Destination"); break;
505                            case 0x88: sprintf((char *)tempbuf, "Move Length"); break;
506                            case 0x89: sprintf((char *)tempbuf, "PID Block Load Report"); break;
507                            case 0x8b: sprintf((char *)tempbuf, "Block Load Status"); break;
508                            case 0x8c: sprintf((char *)tempbuf, "Block Load Success"); break;
509                            case 0x8d: sprintf((char *)tempbuf, "Block Load Full"); break;
510                            case 0x8e: sprintf((char *)tempbuf, "Block Load Error"); break;
511                            case 0x8f: sprintf((char *)tempbuf, "Block Handle"); break;
512                            case 0x90: sprintf((char *)tempbuf, "PID Block Free Report"); break;
513                            case 0x91: sprintf((char *)tempbuf, "Type Specific Block Handle"); break;
514                            case 0x92: sprintf((char *)tempbuf, "PID State Report"); break;
515                            case 0x94: sprintf((char *)tempbuf, "Effect Playing"); break;
516                            case 0x95: sprintf((char *)tempbuf, "PID Device Control Report"); break;
517                            case 0x96: sprintf((char *)tempbuf, "PID Device Control"); break;
518                            case 0x97: sprintf((char *)tempbuf, "DC Enable Actuators"); break;
519                            case 0x98: sprintf((char *)tempbuf, "DC Disable Actuators"); break;
520                            case 0x99: sprintf((char *)tempbuf, "DC Stoop All Effects"); break;
521                            case 0x9a: sprintf((char *)tempbuf, "DC Device Reset"); break;
522                            case 0x9b: sprintf((char *)tempbuf, "DC Device Pause"); break;
523                            case 0x9c: sprintf((char *)tempbuf, "DC Device Continue"); break;
524                            case 0x9f: sprintf((char *)tempbuf, "Device Paused"); break;
525                            case 0xa0: sprintf((char *)tempbuf, "Actuators Enabled"); break;
526                            case 0xa4: sprintf((char *)tempbuf, "Safety Switch"); break;
527                            case 0xa5: sprintf((char *)tempbuf, "Actuator Override Switch"); break;
528                            case 0xa6: sprintf((char *)tempbuf, "Actuator Power"); break;
529                            case 0xa7: sprintf((char *)tempbuf, "Start Delay"); break;
530                            case 0xa8: sprintf((char *)tempbuf, "Parameter Block Size"); break;
531                            case 0xa9: sprintf((char *)tempbuf, "Device Managed Pool"); break;
532                            case 0xaa: sprintf((char *)tempbuf, "Shared parameter blocks"); break;
533                            case 0xab: sprintf((char *)tempbuf, "Create New Effect Report"); break;
534                            case 0xac: sprintf((char *)tempbuf, "RAM Pool Available"); break;
535
536
537                            default: sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value); break;
538                        }
539                        strcat((char *)tempbuf, (char *)")");
540                    }
541                    else
542                    {
543                        sprintf((char *)tempbuf, "%d (0x%x)", (int)value, (unsigned int)value);
544                    }
545
546                    strcat((char *)buf, (char *)tempbuf);
547                    tempbuf[0] = 0;    // we don't want to add this again outside the switch
548                    tempbufvalue[0] = 0;
549                    datahandled = 1;
550                    break;
551
552                case kReport_TagUsageMin: sprintf((char *)tempbuf,        "Usage Minimum........... "); break;
553                case kReport_TagUsageMax: sprintf((char *)tempbuf,        "Usage Maximum........... "); break;
554                case kReport_TagDesignatorIndex: sprintf((char *)tempbuf, "Designator Index........ "); break;
555                case kReport_TagDesignatorMin: sprintf((char *)tempbuf,   "Designator Minumum...... "); break;
556                case kReport_TagDesignatorMax: sprintf((char *)tempbuf,   "Designator Maximum...... "); break;
557                case kReport_TagStringIndex: sprintf((char *)tempbuf,     "String Index............ "); break;
558                case kReport_TagStringMin: sprintf((char *)tempbuf,       "String Minimum.......... "); break;
559                case kReport_TagStringMax: sprintf((char *)tempbuf,       "String Maximum.......... "); break;
560                case kReport_TagSetDelimiter: sprintf((char *)tempbuf,    "Set Delimiter........... "); break;
561            }
562                break;
563
564            case kReport_TypeReserved:
565                sprintf((char *)tempbuf, "Reserved "); break;
566                break;
567        }
568
569        // actually put in the data from the switch -- why not just strcat there??
570        strcat((char *)buf, (char *)tempbuf);
571
572        // if we didn't handle the data before, print in generic fashion
573        if (!datahandled && size)
574        {
575            strcat((char *)bufvalue, (char *)"(");
576            if (size <= 4)
577            {
578                if (usagesigned)
579                {
580                    sprintf((char *)tempbufvalue, "%d", (int32_t)svalue);
581                }
582                else
583                {
584                    sprintf((char *)tempbufvalue, "%u", (uint32_t)value);
585                }
586                strcat((char *)bufvalue, (char *)tempbufvalue);
587            }
588            else
589                for (i = 0; i < size; i++)
590                {
591                    sprintf((char *)tempbufvalue, "%02X ", *(reportDesc++));
592                    strcat((char *)bufvalue, (char *)tempbufvalue);
593                }
594            strcat((char *)bufvalue, (char *)") ");
595        }
596
597
598        // finally add the info
599        strcat((char *)bufvalue, " "); // in case bufvalue was empty, add a blank space
600
601
602        // this juggling is because the End Collection tags were not nested deep enough in the OutlineView.
603
604        /*        if (tag == kReport_TagEndCollection) {
605         [self PrintKeyVal:buf val:bufvalue forDevice:deviceNumber atDepth:depth+indentLevel+1 forNode:node];
606         }
607         else
608         [self PrintKeyVal:buf val:bufvalue forDevice:deviceNumber atDepth:depth+indentLevel forNode:node];*/
609
610        printf("%s%s\n",buf, bufvalue);
611    }
612}
613