TestHIDprobe.c

/*
    File:       TestHIDprobe.c
 
    Contains:   xxx put contents here xxx
 
    Version:    xxx put version here xxx
 
    Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
 
    File Ownership:
 
        DRI:                xxx put dri here xxx
 
        Other Contact:      xxx put other contact here xxx
 
        Technology:         xxx put technology here xxx
 
    Writers:
 
        (BWS)   Brent Schorsch
 
    Change History (most recent first):
 
        <0*>     5/18/98    BWS     stolen from USBProber
*/
 
#include <string.h>
#include <stdio.h>
#include <errors.h>
#include <USB.h>
 
/*  HID Constants - Spec 1.0 */
 
#define UnpackReportSize(packedByte)    ((packedByte) & 0x03)
#define UnpackReportType(packedByte)    (((packedByte) & 0x0C) >> 2)
#define UnpackReportTag(packedByte)     (((packedByte) & 0xF0) >> 4)
 
enum
{
    kReport_TypeMain            = 0,
    kReport_TypeGlobal          = 1,
    kReport_TypeLocal           = 2,
    kReport_TypeReserved        = 3,
    
    kReport_TagLongItem         = 0x0F,
    
    // main items
    kReport_TagInput            = 0x08,
    kReport_TagOutput           = 0x09,
    kReport_TagFeature          = 0x0B,
    kReport_TagCollection       = 0x0A,
    kReport_TagEndCollection    = 0x0C,
    
    // global items
    kReport_TagUsagePage        = 0x00,
    kReport_TagLogicalMin       = 0x01,
    kReport_TagLogicalMax       = 0x02,
    kReport_TagPhysicalMin      = 0x03,
    kReport_TagPhysicalMax      = 0x04,
    kReport_TagUnitExponent     = 0x05,
    kReport_TagUnit             = 0x06,
    kReport_TagReportSize       = 0x07,
    kReport_TagReportID         = 0x08,
    kReport_TagReportCount      = 0x09,
    kReport_TagPush             = 0x0A,
    kReport_TagPop              = 0x0B,
 
    // local items
    kReport_TagUsage            = 0x00,
    kReport_TagUsageMin         = 0x01,
    kReport_TagUsageMax         = 0x02,
    kReport_TagDesignatorIndex  = 0x03,
    kReport_TagDesignatorMin    = 0x04,
    kReport_TagDesignatorMax    = 0x05,
    kReport_TagStringIndex      = 0x07,
    kReport_TagStringMin        = 0x08,
    kReport_TagStringMax        = 0x09,
    kReport_TagSetDelimiter     = 0x0A
};
 
// Collection constants
enum
{
    kCollection_Physical        = 0x00,
    kCollection_Application     = 0x01,
    kCollection_Logical         = 0x02
};
 
// I/O constants (used for Input/Output/Feature tags)
enum
{
    kIO_Data_or_Constant                = 0x0001,
    kIO_Array_or_Variable               = 0x0002,
    kIO_Absolute_or_Relative            = 0x0004,
    kIO_NoWrap_or_Wrap                  = 0x0008,
    kIO_Linear_or_NonLinear             = 0x0010,
    kIO_PreferredState_or_NoPreferred   = 0x0020,
    kIO_NoNullPosition_or_NullState     = 0x0040,
    kIO_NonVolatile_or_Volatile         = 0x0080,       // reserved for Input
    kIO_BitField_or_BufferedBytes       = 0x0100
};
 
// Usage pages from HID Usage Tables spec 1.0
enum
{
    kUsage_PageGenericDesktop       = 0x01,
    kUsage_PageSimulationControls   = 0x02,
    kUsage_PageVRControls           = 0x03,
    kUsage_PageSportControls        = 0x04,
    kUsage_PageGameControls         = 0x05,
    kUsage_PageKeyboard             = 0x07,
    kUsage_PageLED                  = 0x08,
    kUsage_PageButton               = 0x09,
    kUsage_PageOrdinal              = 0x0A,
    kUsage_PageTelephonyDevice      = 0x0B,
    kUsage_PageConsumer             = 0x0C,
    kUsage_PageDigitizers           = 0x0D,
    kUsage_PageUnicode              = 0x10,
    kUsage_PageAlphanumericDisplay  = 0x14
};
 
// Usage constants for Generic Desktop page (01) from HID Usage Tables spec 1.0
enum
{
    kUsage_01_Pointer       = 0x01,
    kUsage_01_Mouse         = 0x02,
    kUsage_01_Joystick      = 0x04,
    kUsage_01_GamePad       = 0x05,
    kUsage_01_Keyboard      = 0x06,
    kUsage_01_Keypad        = 0x07,
    
    kUsage_01_X             = 0x30,
    kUsage_01_Y             = 0x31,
    kUsage_01_Z             = 0x32,
    kUsage_01_Rx            = 0x33,
    kUsage_01_Ry            = 0x34,
    kUsage_01_Rz            = 0x35,
    kUsage_01_Slider        = 0x36,
    kUsage_01_Dial          = 0x37,
    kUsage_01_Wheel         = 0x38,
    kUsage_01_HatSwitch     = 0x39,
    kUsage_01_CountedBuffer = 0x3A,
    kUsage_01_ByteCount     = 0x3B,
    kUsage_01_MotionWakeup  = 0x3C,
    
    kUsage_01_Vx            = 0x40,
    kUsage_01_Vy            = 0x41,
    kUsage_01_Vz            = 0x42,
    kUsage_01_Vbrx          = 0x43,
    kUsage_01_Vbry          = 0x44,
    kUsage_01_Vbrz          = 0x45,
    kUsage_01_Vno           = 0x46,
    
    kUsage_01_SystemControl     = 0x80,
    kUsage_01_SystemPowerDown   = 0x81,
    kUsage_01_SystemSleep       = 0x82,
    kUsage_01_SystemWakeup      = 0x83,
    kUsage_01_SystemContextMenu = 0x84,
    kUsage_01_SystemMainMenu    = 0x85,
    kUsage_01_SystemAppMenu     = 0x86,
    kUsage_01_SystemMenuHelp    = 0x87,
    kUsage_01_SystemMenuExit    = 0x88,
    kUsage_01_SystemMenuSelect  = 0x89,
    kUsage_01_SystemMenuRight   = 0x8A,
    kUsage_01_SystemMenuLeft    = 0x8B,
    kUsage_01_SystemMenuUp      = 0x8C,
    kUsage_01_SystemMenuDown    = 0x8D
};
 
/*  end HID Constants Spec 1.0  */
 
void PrintHIDReport(UInt8 * reportDesc, UInt32 length);
 
void PrintHIDReport(UInt8 * reportDesc, UInt32 length)
{
    UInt8 *         currByte;
    UInt8 *         end;
    UInt8           size, type, tag;
    UInt32          usagePage = 0;
    UInt32          value;
    SInt32          svalue;
    unsigned char   buf[256], tempbuf[256];
    int             i, k, indentLevel;
    Boolean         datahandled;
    Boolean         usagesigned;
    
    end = reportDesc + length;
    
    printf("Report Descriptor\n");
    printf("0x%x (%d) Bytes\n", length, length);
    
    k= 0;
    buf[0] = 0;
    
    currByte = reportDesc;
    for(i=0; i < length + 3; i++)
    {
        if (!(k%16))
        {
            sprintf((char *)buf, "%04X:", k);
        };
        sprintf((char *)tempbuf, "%02X ", *(currByte++));
        strcat((char *)buf, (char *)tempbuf);
        
        k++;
        if (!(k%16))
        {
            printf("%s\n", (char *)buf);
        };
    };
    if ((k%16))
        printf("%s\n", (char *)buf);
        
    buf[0] = 0;
    indentLevel = 0;
    
    while (reportDesc < end)
    {
        size = UnpackReportSize(*reportDesc);
        if (size == 3) size = 4;    // 0 == 0 bytes, 1 == 1 bytes, 2 == 2 bytes, but 3 == 4 bytes
        
        type = UnpackReportType(*reportDesc);
        tag = UnpackReportTag(*reportDesc);
        reportDesc++;
        
        if (tag == kReport_TagLongItem)
        {
            size = *reportDesc++;
            tag = *reportDesc++;
        }
        
        
        // if we're small enough, load the value into a register (byte swaping)
        if (size <= 4)
        {
            value = 0;
            for (i = 0; i < size; i++)
                value += (*(reportDesc++)) << (i * 8);
            
            svalue = 0;
            switch (size)
            {
                case 1: svalue = (SInt8) value; break;
                case 2: svalue = (SInt16) value; break;
                
                // if the top bit is set, then sign extend it and fall thru to 32bit case
                case 3: if (value & 0x00800000) value |= 0xFF000000; // no break
                case 4: svalue = (SInt32) value; break;
            }
        }
 
        // indent this line
        buf[0] = 0;
        for (i = 0; i < indentLevel; i++)
            strcat((char *)buf, "  ");
        
        
        // get the name of this tag, and do any specific data handling
        datahandled = false;
        switch (type)
        {
            case kReport_TypeMain:
            switch (tag)
            {
                case kReport_TagInput:
                case kReport_TagOutput:
                case kReport_TagFeature:
                    switch (tag)
                    {
                        case kReport_TagInput: strcat((char *)buf, "Input "); break;
                        case kReport_TagOutput: strcat((char *)buf, "Output "); break;
                        case kReport_TagFeature: strcat((char *)buf, "Feature "); break;
                    }
 
                    strcat((char *)buf, (char *)"(");
                    if (value & kIO_Data_or_Constant) strcat((char *)buf, "Constant");
                    else strcat((char *)buf, "Data");
                    
                    if (value & kIO_Array_or_Variable) strcat((char *)buf, ", Variable");
                    else strcat((char *)buf, ", Array");
 
                    if (value & kIO_Absolute_or_Relative) strcat((char *)buf, ", Relative");
                    else strcat((char *)buf, ", Absolute");
 
                    if (value & kIO_NoWrap_or_Wrap) strcat((char *)buf, ", Null State");
                    
                    if (value & kIO_Linear_or_NonLinear) strcat((char *)buf, ", Nonlinear");
                    
                    if (value & kIO_PreferredState_or_NoPreferred) strcat((char *)buf, ", No Preferred");
                    
                    if (value & kIO_NoNullPosition_or_NullState) strcat((char *)buf, ", Null State");
                    
                    if (tag != kReport_TagInput)
                        if (value & kIO_NonVolatile_or_Volatile) strcat((char *)buf, ", Volatile");
                        else strcat((char *)buf, ", Non-volatile");
 
                    if (value & kIO_BitField_or_BufferedBytes) strcat((char *)buf, ", Buffered bytes");
                    strcat((char *)buf, (char *)")");
                    
                    tempbuf[0] = 0; // we don't want to add this again outside the switch
                    datahandled = true;
                    break;
                    
                
                case kReport_TagCollection:
                    indentLevel++; 
                    
                    sprintf((char *)tempbuf, "Collection "); 
                    strcat((char *)buf, (char *)tempbuf);
 
                    strcat((char *)buf, (char *)"(");
                    switch (value)
                    {
                        case kCollection_Physical: sprintf((char *)tempbuf, "Physical"); break;
                        case kCollection_Application: sprintf((char *)tempbuf, "Application"); break;
                        case kCollection_Logical: sprintf((char *)tempbuf, "Logical"); break;
                    }
                    strcat((char *)buf, (char *)tempbuf);
                    strcat((char *)buf, (char *)")");
                    
                    tempbuf[0] = 0; // we don't want to add this again outside the switch
                    datahandled = true;
                    break;
 
                case kReport_TagEndCollection: 
                    // recalc indentation, since we want this line to start earlier
                    indentLevel--;
                    buf[0] = 0;
                    for (i = 0; i < indentLevel; i++)
                        strcat((char *)buf, "  ");
                    
                    sprintf((char *)tempbuf, "End Collection "); 
                    break;
            }
            break;
            
            case kReport_TypeGlobal:
            switch (tag)
            {
                case kReport_TagUsagePage:
                    sprintf((char *)tempbuf, "Usage Page ");
                    strcat((char *)buf, (char *)tempbuf);
 
                    usagesigned = true;
                    usagePage = value;
                    strcat((char *)buf, (char *)"(");
                    switch (usagePage)
                    {
                        case kUsage_PageGenericDesktop: sprintf((char *)tempbuf, "Generic Desktop"); break;
                        case kUsage_PageSimulationControls: sprintf((char *)tempbuf, "Simulation Controls"); break;
                        case kUsage_PageVRControls: sprintf((char *)tempbuf, "VR Controls"); break;
                        case kUsage_PageSportControls: sprintf((char *)tempbuf, "Sports Controls"); break;
                        case kUsage_PageGameControls: sprintf((char *)tempbuf, "Game Controls"); break;
                        case kUsage_PageKeyboard: 
                                sprintf((char *)tempbuf, "Keyboard/Keypad"); 
                                usagesigned = false;
                                break;
                                
                        case kUsage_PageLED: sprintf((char *)tempbuf, "LED"); break;
                        case kUsage_PageButton: sprintf((char *)tempbuf, "Button"); break;
                        case kUsage_PageOrdinal: sprintf((char *)tempbuf, "Ordinal"); break;
                        case kUsage_PageTelephonyDevice: sprintf((char *)tempbuf, "Telephany Device"); break;
                        case kUsage_PageConsumer: sprintf((char *)tempbuf, "Consumer"); break;
                        case kUsage_PageDigitizers: sprintf((char *)tempbuf, "Digitizer"); break;
                        case kUsage_PageUnicode: sprintf((char *)tempbuf, "Unicode"); break;
                        case kUsage_PageAlphanumericDisplay: sprintf((char *)tempbuf, "Alphanumeric Display"); break;
                        
                        default: sprintf((char *)tempbuf, "%d", usagePage); break;
                    }
                    strcat((char *)buf, (char *)tempbuf);
                    strcat((char *)buf, (char *)")");
                    tempbuf[0] = 0; // we don't want to add this again outside the switch
                    datahandled = true;
                    break;
                
                case kReport_TagLogicalMin: sprintf((char *)tempbuf,      "Logical Minimum.... "); break;
                case kReport_TagLogicalMax: sprintf((char *)tempbuf,      "Logical Maximum.... "); break;
                case kReport_TagPhysicalMin: sprintf((char *)tempbuf,     "Physical Minimum... "); break;
                case kReport_TagPhysicalMax: sprintf((char *)tempbuf,     "Physical Maximum... "); break;
                case kReport_TagUnitExponent: sprintf((char *)tempbuf,    "Unit Exponent...... "); break;
                case kReport_TagUnit: sprintf((char *)tempbuf,            "Unit............... "); break;
                case kReport_TagReportSize: sprintf((char *)tempbuf,      "Report Size........ "); break;
                case kReport_TagReportID: sprintf((char *)tempbuf,        "ReportID........... "); break;
                case kReport_TagReportCount: sprintf((char *)tempbuf,     "Report Count....... "); break;
                case kReport_TagPush: sprintf((char *)tempbuf,            "Push............... "); break;
                case kReport_TagPop: sprintf((char *)tempbuf,             "Pop................ "); break;
            }
            break;
            
            case kReport_TypeLocal:
            switch (tag)
            {
                case kReport_TagUsage:
                    sprintf((char *)tempbuf, "Usage ");
                    strcat((char *)buf, (char *)tempbuf);
                    strcat((char *)buf, (char *)"(");
                    switch (value)
                    {
                        case kUsage_01_Pointer: sprintf((char *)tempbuf, "Pointer"); break;
                        case kUsage_01_Mouse: sprintf((char *)tempbuf, "Mouse"); break;
                        case kUsage_01_Joystick: sprintf((char *)tempbuf, "Joystick"); break;
                        case kUsage_01_GamePad: sprintf((char *)tempbuf, "GamePad"); break;
                        case kUsage_01_Keyboard: sprintf((char *)tempbuf, "Keyboard"); break;
                        case kUsage_01_Keypad: sprintf((char *)tempbuf, "Keypad"); break;
 
                        case kUsage_01_X: sprintf((char *)tempbuf, "X"); break;
                        case kUsage_01_Y: sprintf((char *)tempbuf, "Y"); break;
                        case kUsage_01_Z: sprintf((char *)tempbuf, "Z"); break;
                        case kUsage_01_Rx: sprintf((char *)tempbuf, "Rx"); break;
                        case kUsage_01_Ry: sprintf((char *)tempbuf, "Ry"); break;
                        case kUsage_01_Rz: sprintf((char *)tempbuf, "Rz"); break;
                        case kUsage_01_Slider: sprintf((char *)tempbuf, "Slider"); break;
                        case kUsage_01_Dial: sprintf((char *)tempbuf, "Dial"); break;
                        case kUsage_01_Wheel: sprintf((char *)tempbuf, "Wheel"); break;
                        case kUsage_01_HatSwitch: sprintf((char *)tempbuf, "Hat Switch"); break;
                        case kUsage_01_CountedBuffer: sprintf((char *)tempbuf, "Counted Buffer"); break;
                        case kUsage_01_ByteCount: sprintf((char *)tempbuf, "Byte Count"); break;
                        case kUsage_01_MotionWakeup: sprintf((char *)tempbuf, "Motion Wakeup"); break;
 
                        case kUsage_01_Vx: sprintf((char *)tempbuf, "Vx"); break;
                        case kUsage_01_Vy: sprintf((char *)tempbuf, "Vy"); break;
                        case kUsage_01_Vz: sprintf((char *)tempbuf, "Vz"); break;
                        case kUsage_01_Vbrx: sprintf((char *)tempbuf, "Vbrx"); break;
                        case kUsage_01_Vbry: sprintf((char *)tempbuf, "Vbry"); break;
                        case kUsage_01_Vbrz: sprintf((char *)tempbuf, "Vbrz"); break;
                        case kUsage_01_Vno: sprintf((char *)tempbuf, "Vno"); break;
 
                        case kUsage_01_SystemControl: sprintf((char *)tempbuf, "System Control"); break;
                        case kUsage_01_SystemPowerDown: sprintf((char *)tempbuf, "System Power Down"); break;
                        case kUsage_01_SystemSleep: sprintf((char *)tempbuf, "System Sleep"); break;
                        case kUsage_01_SystemWakeup: sprintf((char *)tempbuf, "System Wakeup"); break;
                        case kUsage_01_SystemContextMenu: sprintf((char *)tempbuf, "System Context Menu"); break;
                        case kUsage_01_SystemMainMenu: sprintf((char *)tempbuf, "System Main Menu"); break;
                        case kUsage_01_SystemAppMenu: sprintf((char *)tempbuf, "System App Menu"); break;
                        case kUsage_01_SystemMenuHelp: sprintf((char *)tempbuf, "System Menu Help"); break;
                        case kUsage_01_SystemMenuExit: sprintf((char *)tempbuf, "System Menu Exit"); break;
                        case kUsage_01_SystemMenuSelect: sprintf((char *)tempbuf, "System Menu Select"); break;
                        case kUsage_01_SystemMenuRight: sprintf((char *)tempbuf, "System Menu Right"); break;
                        case kUsage_01_SystemMenuLeft: sprintf((char *)tempbuf, "System Menu Left"); break;
                        case kUsage_01_SystemMenuUp: sprintf((char *)tempbuf, "System Menu Up"); break;
                        case kUsage_01_SystemMenuDown: sprintf((char *)tempbuf, "System Menu Down"); break;
                        
                        default: sprintf((char *)tempbuf, "%d", value); break;
                    }
                    strcat((char *)buf, (char *)tempbuf);
                    strcat((char *)buf, (char *)")");
                    tempbuf[0] = 0; // we don't want to add this again outside the switch
                    datahandled = true;
                    break;
                
                case kReport_TagUsageMin: sprintf((char *)tempbuf,        "Usage Minimum...... "); break;
                case kReport_TagUsageMax: sprintf((char *)tempbuf,        "Usage Maximum...... "); break;
                case kReport_TagDesignatorIndex: sprintf((char *)tempbuf, "Designator Index... "); break;
                case kReport_TagDesignatorMin: sprintf((char *)tempbuf,   "Designator Minumum. "); break;
                case kReport_TagDesignatorMax: sprintf((char *)tempbuf,   "Designator Maximum. "); break;
                case kReport_TagStringIndex: sprintf((char *)tempbuf,     "String Index....... "); break;
                case kReport_TagStringMin: sprintf((char *)tempbuf,       "String Minimum..... "); break;
                case kReport_TagStringMax: sprintf((char *)tempbuf,       "String Maximum..... "); break;
                case kReport_TagSetDelimiter: sprintf((char *)tempbuf,    "Set Delimiter...... "); break;
            }
            break;
            
            case kReport_TypeReserved:
            sprintf((char *)tempbuf, "Reserved "); break;
            break;
        }
        
        // actually put in the data from the switch -- why not just strcat there??
        strcat((char *)buf, (char *)tempbuf);
        
        // if we didn't handle the data before, print in generic fashion
        if (!datahandled && size)
        {
            strcat((char *)buf, (char *)"(");
            if (size <= 4)
            {
                if (usagesigned)
                {
                    sprintf((char *)tempbuf, "%ld", (SInt32)svalue);
                }
                else
                {
                    sprintf((char *)tempbuf, "%lu", (UInt32)value);
                }
                strcat((char *)buf, (char *)tempbuf);
            }
            else
                for (i = 0; i < size; i++)
                {
                    sprintf((char *)tempbuf, "%02X ", *(reportDesc++));
                    strcat((char *)buf, (char *)tempbuf);
                }
            strcat((char *)buf, (char *)") ");
        }
        
        // finally add the info
//      printf("ITEM (%s)\n", (char *) tempbuf);
        printf("%s\n", (char *) buf);
    }
}