#define __BINGEN_C__ #include #include #include #include #include #include #include #include #include #include #include #include #include "local.h" #include "item.h" #include "binfile.h" #include "datatbl.h" /* // This is the one global variable that is used by multiple modules in the DLL.. // It exists because we need to use the instance value of this DLL to retrieve // the resources for the settins */ HINSTANCE g_DLLInstance; static GENERATE_OPTIONS generate_opts; static PUCHAR g_ReportBuffer = NULL; static ULONG g_MaxBufferLength = 0; static ULONG g_CurrBufferLength = 0; #define BUFFER_INIT_SIZE 256 #define BUFFER_INCREMENT_SIZE 128 #define OPTIONS_COLL_DEPTH generate_opts.copts.ulMaxCollectionDepth #define OPTIONS_COLL_TOP generate_opts.copts.ulTopLevelCollections #define OPTIONS_COLL_ITEMS_MIN generate_opts.copts.ulMinCollectionItems #define OPTIONS_COLL_ITEMS_MAX generate_opts.copts.ulMaxCollectionItems #define OPTIONS_REPORT_ID_MAX generate_opts.copts.ulMaxReportIDs #define OPTIONS_USE_REPORT_IDS generate_opts.copts.fUseReportIDs #define OPTIONS_USAGEPAGE_MIN generate_opts.uopts.ulMinUsagePage #define OPTIONS_USAGEPAGE_MAX generate_opts.uopts.ulMaxUsagePage #define OPTIONS_USAGE_MIN generate_opts.uopts.ulMinUsage #define OPTIONS_USAGE_MAX generate_opts.uopts.ulMaxUsage #define OPTIONS_CREATE_FIELDS generate_opts.ropts.fCreateDataFields #define OPTIONS_REPORT_SIZE_MIN generate_opts.ropts.ulMinReportSize #define OPTIONS_REPORT_SIZE_MAX generate_opts.ropts.ulMaxReportSize #define OPTIONS_REPORT_COUNT_MIN generate_opts.ropts.ulMinReportCount #define OPTIONS_REPORT_COUNT_MAX generate_opts.ropts.ulMaxReportCount #define OPTIONS_CREATE_BUTTONS generate_opts.ropts.fCreateButtonBitmaps #define OPTIONS_BUTTONS_MIN generate_opts.ropts.ulMinNumButtons #define OPTIONS_BUTTONS_MAX generate_opts.ropts.ulMaxNumButtons #define OPTIONS_CREATE_ARRAYS generate_opts.ropts.fCreateArrays #define OPTIONS_ARRAY_SIZE_MIN generate_opts.ropts.ulMinArraySize #define OPTIONS_ARRAY_SIZE_MAX generate_opts.ropts.ulMaxArraySize #define OPTIONS_ARRAY_COUNT_MIN generate_opts.ropts.ulMinArrayCount #define OPTIONS_ARRAY_COUNT_MAX generate_opts.ropts.ulMaxArrayCount #define OPTIONS_ARRAY_USAGES_MIN generate_opts.ropts.ulMinArrayUsages #define OPTIONS_ARRAY_USAGES_MAX generate_opts.ropts.ulMaxArrayUsages #define OPTIONS_CREATE_BUFFER generate_opts.ropts.fCreateBufferedBytes #define OPTIONS_BUFFER_MIN generate_opts.ropts.ulMinBufferSize #define OPTIONS_BUFFER_MAX generate_opts.ropts.ulMaxBufferSize /* // Added definition since changing function SelectOption to SelectUlongNum but // don't want to change all references in code */ #define SelectOption(o1, o2) SelectUlongNum(o1, o2) #define IN_USAGE_RANGE(u, umin, umax) (((umin) <= (u)) && ((u) <= (umax))) #define IS_VALUE_ITEM(it) ((MI_DATA == ((it) -> miType)) && \ ((FD_DATA == ((it) -> fType)) || \ (FD_BUFFER == ((it) -> fType)) \ ) \ ) #define IS_BUTTON_ITEM(it) ((MI_DATA == ((it) -> miType)) && \ ((FD_ARRAY == ((it) -> fType)) || \ (FD_BUTTONS == ((it) -> fType)) \ ) \ ) #define INIT_LINK_COLL_INDICES() (NextCollIndex = 0) #define GET_LINK_COLL_INDEX() (NextCollIndex++) static USHORT NextCollIndex; /* // This array maps the index values for each tag index defined in bingen.h to // the corresponding tag value that is written to the disk. This structure // is VERY dependent on the order of indices that is defined in bingen.h */ static BYTE TagValues[NUM_INDICES] = { ITEM_TAG_USAGE_PAGE, ITEM_TAG_LOGICAL_MIN, ITEM_TAG_LOGICAL_MAX, ITEM_TAG_PHYSICAL_MIN, ITEM_TAG_PHYSICAL_MAX, ITEM_TAG_UNIT, ITEM_TAG_EXPONENT, ITEM_TAG_REPORT_SIZE, ITEM_TAG_REPORT_COUNT, ITEM_TAG_REPORT_ID, ITEM_TAG_USAGE, ITEM_TAG_USAGE_MIN, ITEM_TAG_USAGE_MAX, ITEM_TAG_DESIGNATOR, ITEM_TAG_DESIGNATOR_MIN, ITEM_TAG_DESIGNATOR_MAX, ITEM_TAG_STRING, ITEM_TAG_STRING_MIN, ITEM_TAG_STRING_MAX, ITEM_TAG_INPUT, ITEM_TAG_OUTPUT, ITEM_TAG_FEATURE, ITEM_TAG_COLLECTION, ITEM_TAG_END_COLLECTION, ITEM_TAG_DELIMITER_OPEN, ITEM_TAG_DELIMITER_CLOSE, ITEM_TAG_PUSH, ITEM_TAG_POP }; /* // All the global structures used in tracking the use of ReportIDs between // and the generation of collections. */ static BOOL fUseReportIDs; static ULONG nApplColls; static ULONG nTotalAvailIDs; static ULONG nMaxAvailCollIDs; BOOLEAN __stdcall DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved ) { switch (dwReason) { case DLL_PROCESS_ATTACH: g_DLLInstance = hinst; default: return TRUE; } } /* // GenerateBIN is the main entry routine for the binary generator. It // takes the opts argument which contains all the user specified options // for generating binaries including the filename for the binary. */ BOOL __stdcall BINGEN_GenerateReportDescriptor( IN GENERATE_OPTIONS *opts, OUT PREPORT_DESC *ReportDesc ) { ULONG MaxColls; ULONG Index; static BOOL fFirstSeed = TRUE; /* // Save the options that were passed in. */ generate_opts = *opts; /* // Initialize the data table that tracks the reportIDs/ReportTypes */ DataTable_InitTable(); /* // Set the random generators seen if need be */ if (fFirstSeed) { srand ( (unsigned) time(NULL)); fFirstSeed = FALSE; } /* // Next step is to determine exactly how many top level collections // will be created for this report descriptor. If more than one top // level collection is to be generated, we MUST use report IDs. If // only one such collection will be output, we can then decide whether // or not we will use ReportIDs. We must also consider the possibility // that all ReportIDs will be closed after creating one or more top-level // collections. In that case, we must bail because we cannot create // any data items within that collection. */ /* // To determine the number of application collections we need to implement, // we need to base it off of both the OPTION_COLL_TOP that the user set // and the number of ReportIDs they said to use. If there # report IDs is // less that OPTIONS_COLL_TOP, that value must used instead since each report // ID then must be used for each collection */ if (!OPTIONS_USE_REPORT_IDS) { MaxColls = 1; } else { MaxColls = (OPTIONS_REPORT_ID_MAX < OPTIONS_COLL_TOP) ? OPTIONS_REPORT_ID_MAX : OPTIONS_COLL_TOP; } nApplColls = SelectUlongNum(1, MaxColls); assert (1 <= nApplColls && OPTIONS_COLL_TOP >= nApplColls); if (1 < nApplColls) { fUseReportIDs = TRUE; } else { fUseReportIDs = SelectOption(FALSE, 1 != nApplColls); } nTotalAvailIDs = fUseReportIDs ? OPTIONS_REPORT_ID_MAX : 1; /* // Now that it has been decided how many Application level collections // will be created and whether or not ReportIDs will be used, we can // start generating and outputting the top level collections */ /* // Begin by allocating the space for our report descriptor structure that // was passed in, we will attempt to allocate space to hold nApplColls // structures. */ *ReportDesc = (PREPORT_DESC) malloc(sizeof(REPORT_DESC)+nApplColls*sizeof(PMAIN_ITEM)); if (NULL == *ReportDesc) { return (FALSE); } for (Index = 0; Index < nApplColls; Index++) { (*ReportDesc) -> Collections[Index] = GenerateCollection(TRUE); if (NULL == (*ReportDesc) -> Collections[Index]) { break; } } (*ReportDesc) -> NumberCollections = Index; DataTable_DestroyTable(); return (TRUE); } BOOL __stdcall BINGEN_GenerateBIN(LPCSTR filename, GENERATE_OPTIONS *opts) { PREPORT_DESC pRD; BOOL fStatus; fStatus = BINGEN_GenerateReportDescriptor(opts, &pRD); if (fStatus) { fStatus = BINGEN_OutputReportDescriptorToDisk(pRD, filename); BINGEN_FreeReportDescriptor(pRD); } return (fStatus); } PMAIN_ITEM GenerateMainItem(void) { PMAIN_ITEM pMI; MAINITEM_TYPE miType; /* // Determine randomly if this option is going to be a collection or a // data item */ miType = (MAINITEM_TYPE) SelectOption(MI_COLLECTION, MI_DATA); switch (miType) { case MI_COLLECTION: /* // Attempt to create a collection. If NULL is returned, it's for // one of two reason, either collection level is too deep or out // of memory. If NULL is returned, we'll try to create a data field // instead. If this is also NULL, it was out of memory not collection // level depth for GenerateCollection failure. Doing this means that // a NULL returned from this function indicates out of memory so calling // functions can process accordingly. */ if (NULL != (pMI = GenerateCollection(FALSE))) { break; } case MI_DATA: pMI = GenerateDataField(SelectOption(HidP_Input, HidP_Feature)); break; default: assert(0); pMI = NULL; break; } return (pMI); } PMAIN_ITEM GenerateConstant( ULONG ConstantSize, ULONG ReportID, HIDP_REPORT_TYPE ReportType ) { PMAIN_ITEM pMI; pMI = (PMAIN_ITEM) malloc(sizeof(MAIN_ITEM)); if (NULL != pMI) { pMI -> ReportType = ReportType; pMI -> fType = FD_CONSTANT; pMI -> ulReportID = ReportID; /* // NOTE: Possible improvement here...It'd be interesting to // see what effect the breaking down of this constant // size into multiple ReportSize fields would have but // all have the same constants. Will worry about how // to do that at a future date. */ pMI -> ulReportSize = ConstantSize; pMI -> ulReportCount = 1; pMI -> miType = MI_DATA; pMI -> fWrap = FALSE; pMI -> fNull = FALSE; pMI -> fPreferred = FALSE; pMI -> fVolatile = FALSE; pMI -> fBuffered = FALSE; pMI -> fAbsolute = FALSE; } return (pMI); } PMAIN_ITEM GenerateDataField(HIDP_REPORT_TYPE ReportType) { PMAIN_ITEM pMI; BOOL fStatus; FIELD_TYPE OptionList[4]; ULONG OptionListLength; /* // Allocate space for our main item structure. If successful, we'll // fill in those fields that are common in all data field types */ pMI = (PMAIN_ITEM) malloc(sizeof(MAIN_ITEM)); if (NULL != pMI) { /* // In creating a data field, there are a couple of fields which are not // unique to the field being created, miType, ReportType, fType and // reportID. We will generate those here and then call the corresponding // field generation routine base on fType. */ pMI -> miType = MI_DATA; pMI -> ReportType = ReportType; OptionListLength = 0; if (OPTIONS_CREATE_FIELDS) { OptionList[OptionListLength++] = FD_DATA; } if (OPTIONS_CREATE_ARRAYS) { OptionList[OptionListLength++] = FD_ARRAY; } if (OPTIONS_CREATE_BUFFER) { OptionList[OptionListLength++] = FD_BUFFER; } if (OPTIONS_CREATE_BUTTONS) { OptionList[OptionListLength++] = FD_BUTTONS; } /* // If there are no data types that can be create, we just leave */ if (0 == OptionListLength) { free(pMI); return (NULL); } pMI -> fType = OptionList[SelectOption(0, OptionListLength-1)]; /* // Get a reportID from the application collection routines...This */ pMI -> ulReportID = GetCollectionID(); /* // Determine which data routine to call... */ switch (pMI -> fType) { case FD_DATA: fStatus = GenerateData(pMI); break; case FD_ARRAY: fStatus = GenerateArray(pMI); break; case FD_BUFFER: fStatus = GenerateBufferedBytes(pMI); break; case FD_BUTTONS: fStatus = GenerateButtons(pMI); break; default: assert(0); } if (!fStatus) { free(pMI); return(NULL); } } return (pMI); } BOOL GenerateData( IN PMAIN_ITEM pMI ) { ULONG AbsBoundary; ULONG GoodBitOffset; ULONG UsageCount; BOOL CreateUsage; /* // Select the report count and report size...We need to verify that // at this point that the size of the field we select will actually // be alignable at some bit offset. We'll repeat the selection process // until we come up with something alignable */ do { pMI -> ulReportSize = SelectOption(OPTIONS_REPORT_SIZE_MIN, OPTIONS_REPORT_SIZE_MAX); pMI -> ulReportCount = SelectOption(OPTIONS_REPORT_COUNT_MIN, OPTIONS_REPORT_COUNT_MAX); } while (!IsAlignmentPossible(0, pMI -> ulReportSize, pMI -> ulReportCount, &GoodBitOffset )); /* // When determining the usages that will be use for the given data field // we need to base this off our report count and cannot generate // more usages than report count. In fact, we will randomly determine // between 1 and ReportCount exactly how many usages we will create */ UsageCount = SelectUlongNum(1, pMI -> ulReportCount); assert (1 <= UsageCount && UsageCount <= pMI -> ulReportCount); CreateUsage = GetUsages(UsageCount, 16, TRUE, &(pMI -> CommonUsagePage), &(pMI -> UsageListLength), &(pMI -> UsageList) ); if (!CreateUsage) { return (FALSE); } /* // Determine the logical minimum and maximum values for this data field // To do so, we need pick a number that will fit into the bits provided // that are available with this data field. 2^(pMI -> ulReportSize-1) // From the absolute boundary, we pick a number to be our logical minimum. // However, since logical minimum must be less than logical maximum, the upper // bound on the range of logical minimum is 1 less than the maximum bound. // For example, if ReportSize is 8, are absolute boundary is 2^7 or 127 which // means our logical min/max range is -127 to 126 and our logical maximum is. // between logMin+1 and 127. */ pMI -> IsLogMinSigned = (BOOL) SelectOption(FALSE, TRUE); assert(TRUE == pMI -> IsLogMinSigned || FALSE == pMI -> IsLogMinSigned); if (pMI -> IsLogMinSigned) { AbsBoundary = (LONG) ((1 << (pMI -> ulReportSize - 1)) - 1); assert ((LONG) AbsBoundary == (LONG) (pow(2, pMI -> ulReportSize-1)) - 1); pMI -> lLogicalMinimum = SelectLongNum(-((LONG) AbsBoundary), (LONG) AbsBoundary-1); pMI -> lLogicalMaximum = SelectLongNum(pMI -> lLogicalMinimum+1, (LONG) AbsBoundary); } else { if (sizeof(ULONG)*8 == pMI -> ulReportSize) { AbsBoundary = ULONG_MAX; } else { AbsBoundary = ((1 << pMI -> ulReportSize) - 1); } assert (AbsBoundary == ( (sizeof(ULONG)*8 == pMI -> ulReportSize) ? ULONG_MAX : (ULONG) (pow(2, pMI -> ulReportSize) - 1))); pMI -> ulLogicalMinimum = SelectUlongNum(0, AbsBoundary-1); pMI -> ulLogicalMaximum = SelectUlongNum(pMI -> ulLogicalMinimum+1, AbsBoundary); } /* // Decide whether or not to use PhysicalMin/Max */ if (SelectOption(FALSE, FALSE)) { /* // Do Physical Minimum/Maximum generation code here */ } else { /* // Set physical max to be less than physical min to tell the item // generator that no value was chosen for these fields. */ pMI -> lPhysicalMinimum = 0; pMI -> lPhysicalMaximum = -1; } /* // Decide whether or not to use unit fields */ if (SelectOption(FALSE, FALSE)) { /* // Do picking of Unit and UnitExp fields */ } else { /* // Zero out the unit fields */ pMI -> lUnit = 0; pMI -> lExponent = 0; } /* // Generate the flag fields. Begin by setting them all initially to FALSE */ /* // Randomize the settings of the different options. Simply need to // randomly choose between TRUE and FALSE */ pMI -> fWrap = (BOOL) SelectOption(FALSE, TRUE); pMI -> fLinear = (BOOL) SelectOption(FALSE, TRUE); pMI -> fNull = (BOOL) SelectOption(FALSE, TRUE); pMI -> fPreferred = (BOOL) SelectOption(FALSE, TRUE); pMI -> fVolatile = (BOOL) SelectOption(FALSE, TRUE); pMI -> fAbsolute = (BOOL) SelectOption(FALSE, TRUE); /* // This field will always be set to FALSE because there is a special // routine for creating buffered byte data fields. */ pMI -> fBuffered = FALSE; return (TRUE); } BOOL GenerateArray( IN PMAIN_ITEM pMI ) { ULONG AbsBoundary; ULONG GoodBitOffset; ULONG UsageCount; BOOL CreateUsage; /* // An array field is not all that different from a data field. The difference // is that it reports usage values instead of actual values in it's fields. // Therefore, we can actually have more usages than the ReportCount we generate // but the usages must be no bigger than can be supported by ReportSize // structure. */ /* // Here's an interesting point about arrays that I had not noticed/understood // before. An array actually reports an Indexed value which corresponds // the to LogicalMinimum/Maximum. The indices that are returned refer to // to the order in which usages are applied. So if the following is described // LogicalMinimum(0) // LogicalMaximum(16) // UsageMin(1) // UsageMax(17) // // index 0 --> Usage 1, index 1 --> usage 2 */ /* // Selecting the report count and report size for arrays is the same // as selecting them for data fields. We just need to verify that // the size/count combination we select is an alignable one. */ do { pMI -> ulReportSize = SelectOption(OPTIONS_ARRAY_SIZE_MIN, OPTIONS_ARRAY_SIZE_MAX); pMI -> ulReportCount = SelectOption(OPTIONS_ARRAY_COUNT_MIN, OPTIONS_ARRAY_COUNT_MAX); } while (!IsAlignmentPossible(0, pMI -> ulReportSize, pMI -> ulReportCount, &GoodBitOffset )); /* // The next step in creating an array is to determine the indexing values // which are done through the logical minimum and logical maximum value // range. The range of index values is based on the ReportSize. The // code is duplicate of that within the data field generation. For now, // however, we will limit the number of indices to 2^16 to compensate // for 16-bit usages. We an probably relax this restriction later on but // my understanding is not complete at this point. Perhaps later... // What we'll actually do is determine how many usages we will report with // this array and then determine a logical minimum and then the logical // maximum will be set to LogMin + UsageCount. */ /* // The usage count that is allowed is limited by 2^16. Theoretically, this // is not so, but for now, we're making it so. */ UsageCount = SelectUlongNum(OPTIONS_ARRAY_USAGES_MIN, OPTIONS_ARRAY_USAGES_MAX); /* // Next we determine how our logical minimum/maximum range will be // defined (signed or unsigned), then the upper and lower boundaries for // that range and then choose the lower number within that boundary. Our // usage count amount will then determine the upper boundary */ pMI -> IsLogMinSigned = (BOOL) SelectOption(FALSE, TRUE); assert(TRUE == pMI -> IsLogMinSigned || FALSE == pMI -> IsLogMinSigned); if (pMI -> IsLogMinSigned) { AbsBoundary = (LONG) ((1 << (pMI -> ulReportSize - 1)) - 1); assert ((LONG) AbsBoundary == (LONG) (pow(2, pMI -> ulReportSize-1)) - 1); pMI -> lLogicalMinimum = SelectLongNum(-((LONG) AbsBoundary), (LONG) AbsBoundary-UsageCount+1); pMI -> lLogicalMaximum = pMI -> lLogicalMinimum + UsageCount - 1; } else { if (sizeof(ULONG)*8 == pMI -> ulReportSize) { AbsBoundary = ULONG_MAX; } else { AbsBoundary = ((1 << pMI -> ulReportSize) - 1); } assert (AbsBoundary == ( (sizeof(ULONG)*8 == pMI -> ulReportSize) ? ULONG_MAX : (ULONG) (pow(2, pMI -> ulReportSize) - 1))); pMI -> ulLogicalMinimum = SelectUlongNum(0, AbsBoundary-UsageCount+1); pMI -> ulLogicalMaximum = pMI -> ulLogicalMinimum + UsageCount - 1; } /* // Now we need to generate the usages that correspond to the indices we // generated above. */ CreateUsage = GetUsages(UsageCount, 16, TRUE, &(pMI -> CommonUsagePage), &(pMI -> UsageListLength), &(pMI -> UsageList) ); if (!CreateUsage) { return (FALSE); } /* // Decide whether or not to use PhysicalMin/Max. Not sure if // arrays can even use these fields. */ if (SelectOption(FALSE, FALSE)) { /* // Do Physical Minimum/Maximum generation code here */ } else { /* // Set physical max to be less than physical min to tell the item // generator that no value was chosen for these fields. */ pMI -> lPhysicalMinimum = 0; pMI -> lPhysicalMaximum = -1; } /* // Decide whether or not to use unit fields. Like Physical Stuff, i'm // not sure we can use unit stuff for arrays. */ if (SelectOption(FALSE, FALSE)) { /* // Do picking of Unit and UnitExp fields */ } else { /* // Zero out the unit fields */ pMI -> lUnit = 0; pMI -> lExponent = 0; } /* // Generate the flag fields. Begin by setting them all initially to FALSE. // TRUE only should apply to data fields. Any other values should be // ignored when dealing with arrays. It'll be interesting though to see // how the parser deals with these flags set and arrays. However, by setting // them to TRUE, we may be overstepping the bounds of the HID spec. */ pMI -> fWrap = (BOOL) SelectOption(FALSE, TRUE ); pMI -> fNull = (BOOL) SelectOption(FALSE, TRUE ); pMI -> fPreferred = (BOOL) SelectOption(FALSE, TRUE); pMI -> fVolatile = (BOOL) SelectOption(FALSE, TRUE ); pMI -> fAbsolute = (BOOL) SelectOption(FALSE, TRUE ); /* // Again, this field should be ignored, so we'll randomize it's usage as well */ pMI -> fBuffered = FALSE; return (TRUE); } BOOL GenerateButtons( IN PMAIN_ITEM pMI ) { BOOL CreateUsage; /* // Select the report count and report size...For button bitmaps, the // report size is always one and the report count is simply selected // from the range specified by the user. There is no need to check for // alignment because something of size 1 bit can spread across more than // one bit, yet alone 32. */ pMI -> ulReportSize = 1; pMI -> ulReportCount = SelectOption(OPTIONS_BUTTONS_MIN, OPTIONS_BUTTONS_MAX); /* // The UsageCount for the button bitmaps must be equal to the report count, // since there must be a defined usage per button within the list box */ CreateUsage = GetUsages(pMI -> ulReportCount, 16, TRUE, &(pMI -> CommonUsagePage), &(pMI -> UsageListLength), &(pMI -> UsageList) ); if (!CreateUsage) { return (FALSE); } /* // The logical Min/MAx for these fields is also very simple to determine // its simply 0 and 1. */ pMI -> IsLogMinSigned = FALSE; pMI -> ulLogicalMinimum = 0; pMI -> ulLogicalMaximum = 1; /* // Will not use PhysicalMinimums/Maximums within buttons. Might want to // try to do so at a later date. */ pMI -> lPhysicalMinimum = 0; pMI -> lPhysicalMaximum = -1; pMI -> lUnit = 0; pMI -> lExponent = 0; /* // Randomly determien all the other flags. They might have an effect on // on list box */ pMI -> fWrap = (BOOL) SelectOption(FALSE, TRUE); pMI -> fLinear = (BOOL) SelectOption(FALSE, TRUE); pMI -> fNull = (BOOL) SelectOption(FALSE, TRUE); pMI -> fPreferred = (BOOL) SelectOption(FALSE, TRUE); pMI -> fVolatile = (BOOL) SelectOption(FALSE, TRUE); pMI -> fAbsolute = (BOOL) SelectOption(FALSE, TRUE); /* // This field will always be set to FALSE because there is a special // routine for creating buffered byte data fields. */ pMI -> fBuffered = FALSE; return (TRUE); } BOOL GenerateBufferedBytes( IN PMAIN_ITEM pMI ) { ULONG AbsBoundary; ULONG UsageCount; BOOL CreateUsage; /* // Need to only randomly select the report count since the report size for // buffered bytes is always 8. Therefore, we need not worry about // alignment either. */ pMI -> ulReportSize = 8; pMI -> ulReportCount = SelectOption(OPTIONS_BUFFER_MIN, OPTIONS_BUFFER_MAX); /* // When determining the usages that will be use for the given data field // we need to base this off our report count and cannot generate // more usages than report count. In fact, we will randomly determine // between 1 and ReportCount exactly how many usages we will create */ UsageCount = SelectUlongNum(1, pMI -> ulReportCount); assert (1 <= UsageCount && UsageCount <= pMI -> ulReportCount); CreateUsage = GetUsages(UsageCount, 16, TRUE, &(pMI -> CommonUsagePage), &(pMI -> UsageListLength), &(pMI -> UsageList) ); if (!CreateUsage) { return (FALSE); } /* // The logical minimum/maximum for a buffered bytes array corresponds to // limits allowed within each of the slots. We'll choose these just like // we chose all the logical minimum/maximum's in the data fields */ pMI -> IsLogMinSigned = (BOOL) SelectOption(FALSE, TRUE); assert(TRUE == pMI -> IsLogMinSigned || FALSE == pMI -> IsLogMinSigned); if (pMI -> IsLogMinSigned) { AbsBoundary = (LONG) ((1 << (pMI -> ulReportSize - 1)) - 1); assert ((LONG) AbsBoundary == (LONG) (pow(2, pMI -> ulReportSize-1)) - 1); pMI -> lLogicalMinimum = SelectLongNum(-((LONG) AbsBoundary), (LONG) AbsBoundary-1); pMI -> lLogicalMaximum = SelectLongNum(pMI -> lLogicalMinimum+1, (LONG) AbsBoundary); } else { if (sizeof(ULONG)*8 == pMI -> ulReportSize) { AbsBoundary = ULONG_MAX; } else { AbsBoundary = ((1 << pMI -> ulReportSize) - 1); } assert (AbsBoundary == ( (sizeof(ULONG)*8 == pMI -> ulReportSize) ? ULONG_MAX : (ULONG) (pow(2, pMI -> ulReportSize) - 1))); pMI -> ulLogicalMinimum = SelectUlongNum(0, AbsBoundary-1); pMI -> ulLogicalMaximum = SelectUlongNum(pMI -> ulLogicalMinimum+1, AbsBoundary); } /* // Decide whether or not to use PhysicalMin/Max */ if (SelectOption(FALSE, FALSE)) { /* // Do Physical Minimum/Maximum generation code here */ } else { /* // Set physical max to be less than physical min to tell the item // generator that no value was chosen for these fields. */ pMI -> lPhysicalMinimum = 0; pMI -> lPhysicalMaximum = -1; } /* // Decide whether or not to use unit fields */ if (SelectOption(FALSE, FALSE)) { /* // Do picking of Unit and UnitExp fields */ } else { /* // Zero out the unit fields */ pMI -> lUnit = 0; pMI -> lExponent = 0; } /* // Randomize the settings of the different options. Simply need to // randomly choose between TRUE and FALSE */ pMI -> fWrap = (BOOL) SelectOption(FALSE, TRUE); pMI -> fLinear = (BOOL) SelectOption(FALSE, TRUE); pMI -> fNull = (BOOL) SelectOption(FALSE, TRUE); pMI -> fPreferred = (BOOL) SelectOption(FALSE, TRUE); pMI -> fVolatile = (BOOL) SelectOption(FALSE, TRUE); pMI -> fAbsolute = (BOOL) SelectOption(FALSE, TRUE); /* // This field will always be set to FALSE because there is a special // routine for creating buffered byte data fields. */ pMI -> fBuffered = TRUE; return (TRUE); } PMAIN_ITEM GenerateCollection(BOOL fIsApplication) { PMAIN_ITEM pMI; BOOL CreateUsage; ULONG ulNumItems; ULONG ulIndex; static ULONG ulCollDepth = 0; ulCollDepth++; if (ulCollDepth > OPTIONS_COLL_DEPTH) { ulCollDepth--; return (NULL); } /* // Start off with between 1 and 10 possible items in the collections */ ulNumItems = SelectOption(OPTIONS_COLL_ITEMS_MIN, OPTIONS_COLL_ITEMS_MAX); assert (ulNumItems >= OPTIONS_COLL_ITEMS_MIN && ulNumItems <= OPTIONS_COLL_ITEMS_MAX); /* // Allocate space for the main item structure and the pointers to its // composed items */ pMI = (PMAIN_ITEM) malloc(sizeof(MAIN_ITEM) + ulNumItems*sizeof(PMAIN_ITEM)); if (NULL != pMI) { pMI -> miType = MI_COLLECTION; /* // Determine the UsagePage and Usage for this collection */ CreateUsage = GetUsages(1, sizeof(USAGE), FALSE, // Must set to FALSE since HIDPARSE // Currently doesn't support // ExtUsages in Collections &(pMI -> CommonUsagePage), &(pMI -> UsageListLength), &(pMI -> UsageList) ); if (!CreateUsage) { free(pMI); return (NULL); } /* // Determine the type of the collection if fIsApplication is false */ if (!fIsApplication) { pMI -> cType = (COLLECTION_TYPE) SelectOption(CT_PHYSICAL, CT_LOGICAL); } else { pMI -> cType = CT_APPLICATION; OpenApplCollection(); } for (ulIndex = 0; ulIndex < ulNumItems; ulIndex++) { if (NULL == (pMI -> ComposedItems[ulIndex] = GenerateMainItem())) { break; } } pMI -> ulNumCompositeItems = ulIndex; if (fIsApplication) { CloseApplCollection(); } } ulCollDepth--; return (pMI); } void FreeReportStructure(PMAIN_ITEM pMI) { ULONG ulIndex; if (MI_COLLECTION == pMI -> miType) { for (ulIndex = 0; ulIndex < pMI -> ulNumCompositeItems; ulIndex++) { FreeReportStructure(pMI -> ComposedItems[ulIndex]); } } free(pMI -> UsageList); free(pMI); return; } VOID __stdcall BINGEN_FreeReportDescriptor( PREPORT_DESC pRD ) { ULONG Index; for (Index = 0; Index < pRD -> NumberCollections; Index++) { FreeReportStructure(pRD -> Collections[Index]); } free( pRD ); return; } VOID __stdcall BINGEN_FreeReportBuffer( PUCHAR ReportBuffer ) { if (NULL != ReportBuffer) free(ReportBuffer); return; } BOOL OutputUsages( IN USAGE CommonUsagePage, IN PUSAGE_LIST UsageList, IN ULONG UsageListLength ) { PUSAGE_LIST CurrUsage; BOOL fStatus; ULONG ExtendedUsage; /* // When generating the Usage information, we need to do the following steps: // 1) Generate the common usage page tag if the value is not 0. // 2) Output each of the usages in the list. If the usagepage is 0 or it's // equal to the CommonUsagePage, then don't output a usagePage tag. // 3) Otherwise, this is an extended and an extended usage must be generated // An extended usage is one of the form, // USAGE (XXXX:YYYY) where XXXX is the USAGE_PAGE and // YYYY is the USAGE */ CurrUsage = UsageList; fStatus = TRUE; if (0 != CommonUsagePage) { fStatus &= OutputItem( TAG_INDEX_USAGE_PAGE, DetermineULongSize(CommonUsagePage), CommonUsagePage ); } while (UsageListLength--) { if (CommonUsagePage != CurrUsage -> UsagePage) { ExtendedUsage = ((CurrUsage -> UsagePage) << 16); if (CurrUsage -> IsMinMax) { fStatus &= OutputItem (TAG_INDEX_USAGE_MIN, sizeof(ULONG), ExtendedUsage | CurrUsage -> UsageMin ); fStatus &= OutputItem (TAG_INDEX_USAGE_MAX, sizeof(ULONG), ExtendedUsage | CurrUsage -> UsageMax ); } else { fStatus &= OutputItem( TAG_INDEX_USAGE, sizeof(ULONG), ExtendedUsage | CurrUsage -> Usage ); } } else { if (CurrUsage -> IsMinMax) { fStatus &= OutputItem( TAG_INDEX_USAGE_MIN, DetermineULongSize(CurrUsage -> UsageMin), CurrUsage -> UsageMin ); fStatus &= OutputItem( TAG_INDEX_USAGE_MAX, DetermineULongSize(CurrUsage -> UsageMax), CurrUsage -> UsageMax ); } else { fStatus &= OutputItem( TAG_INDEX_USAGE, DetermineULongSize(CurrUsage -> Usage), CurrUsage -> Usage ); } } CurrUsage++; } return (fStatus); } BOOL OutputReportStructure( PMAIN_ITEM pMI ) { if (MI_COLLECTION == pMI -> miType) { return(OutputCollection(pMI)); } else { return(OutputDataField(pMI)); } } BOOL __stdcall BINGEN_OutputReportDescriptorToMemory( IN PREPORT_DESC pRD, OUT PUCHAR *ReportBuffer, OUT PULONG ReportBufferLength ) { BOOL fStatus; ULONG Index; assert (NULL != pRD); /* // Attempt to allocate memory for the report buffer. If we can't then // must fail this call */ if (!InitializeReportBuffer()) { *ReportBuffer = NULL; *ReportBufferLength = 0; return (FALSE); } /* // Initialize the data table so that we can track the sizes of report IDs */ DataTable_InitTable(); /* // Output each one of the collections in the structure */ fStatus = TRUE; for (Index = 0; Index < pRD -> NumberCollections; Index++) { fStatus = OutputCollection(pRD -> Collections[Index]) && fStatus; } if (!fStatus) { free(g_ReportBuffer); *ReportBuffer = NULL; *ReportBufferLength = 0; } else { *ReportBuffer = g_ReportBuffer; *ReportBufferLength = g_CurrBufferLength; } DataTable_DestroyTable(); return (fStatus); } BOOL __stdcall BINGEN_OutputReportDescriptorToDisk( IN PREPORT_DESC pRD, OUT LPCSTR filename ) { BIN_FILE outfile; PUCHAR buffer; ULONG bufferLength; BOOL fStatus; /* // In order to write the report descriptor to disk, we'll perform // two steps. // // 1) Output the report descriptor to a buffer in memory // 2) If this succeeds, we'll create a binary file in which to output // the structure to and then we'll block write that data buffer into // memory. */ fStatus = FALSE; if (BINGEN_OutputReportDescriptorToMemory(pRD, &buffer, &bufferLength)) { /* // Output the report descriptor in memory to our binary file. // We will use external routines to write the file to disk since // in other places we may already have a descriptor in memory and // also want to write that memory buffer to disk. No sense // duplicating ourselves. This routine will probably be located // in the standard BIN generation routines. All we need to do // is open up the bin file, write the buffer, close the binfile and // go */ /* // Open the file */ fStatus = fNewBINFile(filename, &outfile); /* // Write the block of memory */ if (fStatus) { fStatus = fBINWriteBuffer(&outfile, buffer, bufferLength); } /* // Close the file */ vCloseBINFile(&outfile); } /* // Free the buffer that was allocated */ if (NULL != buffer) { BINGEN_FreeReportBuffer(buffer); } return (fStatus); } BOOL __stdcall BINGEN_GenerateCaps( IN PREPORT_DESC pRD, IN ULONG TLCNum, OUT PHIDP_CAPS Caps ) { PMAIN_ITEM CurrCollection; BOOL Status; /* // To generate the caps structure, we need to calculate each of the fields // in the HIDP_CAPS structure using the support routines defined below */ if (TLCNum > pRD -> NumberCollections) return (FALSE); CurrCollection = pRD -> Collections[TLCNum-1]; /* // The usage for a application collection should be not be a range */ assert (!CurrCollection -> UsageList -> IsMinMax); Caps -> UsagePage = CurrCollection -> UsageList -> UsagePage; Caps -> Usage = CurrCollection -> UsageList -> Usage; /* // Calculate the report lengths */ Status = CalcReportLengths(CurrCollection, &(Caps -> InputReportByteLength), &(Caps -> OutputReportByteLength), &(Caps -> FeatureReportByteLength) ); /* // Calcluate the number of LinkCollectionNodes */ CountLinkCollNodes(CurrCollection, &(Caps -> NumberLinkCollectionNodes) ); /* // Calculate the number of ValueCaps and ButtonCaps for each ReportType */ CountValueAndButtonCaps(CurrCollection, HidP_Input, &(Caps -> NumberInputValueCaps), &(Caps -> NumberInputButtonCaps) ); CountValueAndButtonCaps(CurrCollection, HidP_Output, &(Caps -> NumberOutputValueCaps), &(Caps -> NumberOutputButtonCaps) ); CountValueAndButtonCaps(CurrCollection, HidP_Feature, &(Caps -> NumberFeatureValueCaps), &(Caps -> NumberFeatureButtonCaps) ); return (Status); } BOOL __stdcall BINGEN_GenerateValueCaps( IN PREPORT_DESC pRD, IN ULONG TLCNum, IN HIDP_REPORT_TYPE ReportType, OUT PHIDP_VALUE_CAPS *ValueCaps, OUT PUSHORT NumValueCaps ) /*++ Routine Description: This allocates space for and fills in a value caps list that corresponds to the report descriptor structure that is input. A caller of this routine must use BINGEN_FreeValueCaps to insure the use of the proper corresponding freeing routine. Arguments: pRD - Report descriptor structure from which the value caps list is to be generated TLCNum - A one-based index of the top-level collection to generate the list from Remember a report descriptor structure can have have multiple TLCs where each one would be treated as a separate device by HIDCLASS and so there exists a unique value caps list per TLC ReportType - Report type for which to generate the value caps structure ValueCaps - Function allocated list of value caps structures NumCaps - Number of value caps structures in the list Return Value: TRUE if list creation succeeded FALSE otherwise - ValueCaps = NULL and NumCaps = 0 in this case --*/ { USHORT ButtonCount; USHORT ValueCount; USHORT nFilled; /* // Check to insure that the TLCNumber requested is within the number // of TLCs for this report descriptor */ if (TLCNum > pRD -> NumberCollections) { return (FALSE); } CountValueAndButtonCaps(pRD -> Collections[TLCNum-1], ReportType, &ValueCount, &ButtonCount ); *NumValueCaps = 0; *ValueCaps = (PHIDP_VALUE_CAPS) calloc(ValueCount, sizeof(HIDP_VALUE_CAPS)); /* // If the memory allocation succeeds, fill in the value caps structures */ if (NULL != *ValueCaps) { FillValueCaps( pRD -> Collections[TLCNum-1], 0, ReportType, *ValueCaps, &nFilled ); assert (nFilled == ValueCount); *NumValueCaps = ValueCount; } return (NULL != *ValueCaps); } VOID __stdcall BINGEN_FreeValueCaps( IN PHIDP_VALUE_CAPS ValueCaps ) /*++ Routine Description: This routine frees a list of value caps that that was allocated by the BINGEN_GenerateValueCaps routine since the DLL may use a different set of allocation routines than the application which uses the DLL. Arguments: ValueCaps - The list of value caps to free Return Value: --*/ { if (NULL != ValueCaps) { free(ValueCaps); } return; } BOOL __stdcall BINGEN_GenerateButtonCaps( IN PREPORT_DESC pRD, IN ULONG TLCNum, IN HIDP_REPORT_TYPE ReportType, OUT PHIDP_BUTTON_CAPS *ButtonCaps, OUT PUSHORT NumButtonCaps ) /*++ Routine Description: This allocates space for and fills in a HIDP_BUTTON_CAPS list that corresponds to the report descriptor structure that is input. A caller of this routine must use BINGEN_FreeButtonCaps to insure the use of the proper corresponding freeing routine. Arguments: pRD - Report descriptor structure from which the button caps list is to be generated TLCNum - A one-based index of the top-level collection to generate the list from Remember a report descriptor structure can have have multiple TLCs where each one would be treated as a separate device by HIDCLASS and so there exists a unique button caps list per TLC ReportType - Report type for which to generate the button caps structure ValueCaps - Function allocated list of button caps structures NumCaps - Number of button caps structures in the list Return Value: TRUE if list creation succeeded FALSE otherwise - ButtonCaps = NULL and NumCaps = 0 in this case --*/ { USHORT ButtonCount; USHORT ValueCount; USHORT nFilled; USHORT CollNum; /* // Check to insure that the TLCNumber requested is within the number // of TLCs for this report descriptor */ if (TLCNum > pRD -> NumberCollections) { return (FALSE); } CountValueAndButtonCaps(pRD -> Collections[TLCNum-1], ReportType, &ValueCount, &ButtonCount ); *NumButtonCaps = 0; *ButtonCaps = (PHIDP_BUTTON_CAPS) calloc(ButtonCount, sizeof(HIDP_BUTTON_CAPS)); /* // If the memory allocation succeeds, fill in the button caps structures */ if (NULL != *ButtonCaps) { CollNum = 0; FillButtonCaps(pRD -> Collections[TLCNum-1], &CollNum, ReportType, *ButtonCaps, &nFilled ); assert (nFilled == ButtonCount); *NumButtonCaps = ButtonCount; } return (NULL != *ButtonCaps); } VOID __stdcall BINGEN_FreeButtonCaps( IN PHIDP_BUTTON_CAPS ButtonCaps ) /*++ Routine Description: This routine frees a list of value caps that that was allocated by the BINGEN_GenerateButtonCaps routine since the DLL may use a different set of allocation routines than the application which uses the DLL. Arguments: ButtonCaps - The list of button caps to free Return Value: --*/ { if (NULL != ButtonCaps) { free(ButtonCaps); } return; } BOOL __stdcall BINGEN_GenerateLinkCollectionNodes( IN PREPORT_DESC pRD, IN ULONG TLCNum, OUT PHIDP_LINK_COLLECTION_NODE *Nodes, OUT PUSHORT NumNodes ) /*++ Routine Description: This allocates space for and fills in a link collection node list that corresponds to the report descriptor structure that is input. A caller of this routine must use Bingen_FreeLinkCollectionNodes to insure the use of the proper corresponding freeing routine. Arguments: pRD - Report descriptor structure from which the node list is to be generated TLCNum - A one-based index of the top-level collection to generate the list from Remember a report descriptor structure can have have multiple TLCs where each one would be treated as a separate device by HIDCLASS and so there exists a unique LinkCollectionNode list per TLC Nodes - Function allocated list of link collection node structures NumNodes - Number of link collection nodes in the list Return Value: TRUE if list creation succeeded FALSE otherwise - Nodes == NULL and NumNodes == 0 in this case --*/ { USHORT NumLC; USHORT nFilled; /* // Check to insure that the TLCNumber requested is within the number // of TLCs for this report descriptor */ if (TLCNum > pRD -> NumberCollections) { return (FALSE); } CountLinkCollNodes(pRD -> Collections[TLCNum-1], &NumLC ); *NumNodes = 0; *Nodes = (PHIDP_LINK_COLLECTION_NODE) calloc(NumLC, sizeof(HIDP_LINK_COLLECTION_NODE)); /* // If the memory allocation succeeds, fill in the node structure */ if (NULL != *Nodes) { FillLinkNodes(pRD -> Collections[TLCNum-1], *Nodes, 0, 0, &nFilled ); assert (nFilled == (ULONG) NumLC); *NumNodes = NumLC; } return (NULL != *Nodes); } VOID __stdcall BINGEN_FreeLinkCollectionNodes( IN PHIDP_LINK_COLLECTION_NODE Nodes ) /*++ Routine Description: This routine frees a list of link collection nodes that was allocated by Bingen_GenerateLinkCollectionNodes. There is a separate free routine since the DLL may use a different set of allocation routines than the application which uses the DLL. Arguments: Nodes - The list of collection nodes to free up Return Value: --*/ { /* * Make no assumptions that the node list is not NULL */ if (NULL != Nodes) { free(Nodes); } return; } BOOL OutputCollection(PMAIN_ITEM pMI) { ULONG ulIndex; BOOL fStatus = TRUE; fStatus &= OutputUsages(pMI -> CommonUsagePage, pMI -> UsageList, pMI -> UsageListLength ); fStatus &= OutputItem(TAG_INDEX_COLLECTION, 1, CT_APPLICATION == pMI -> cType ? ITEM_COLLECTION_APPLICATION : CT_PHYSICAL == pMI -> cType ? ITEM_COLLECTION_PHYSICAL : ITEM_COLLECTION_LOGICAL ); for (ulIndex = 0; ulIndex < pMI -> ulNumCompositeItems; ulIndex++) { fStatus &= OutputReportStructure(pMI -> ComposedItems[ulIndex]); } /* // If we are generating an application collection, we need to align all // data sizes, close all report IDs associated with this collection and // then, output and end collection item */ if (CT_APPLICATION == pMI -> cType) { fStatus &= AlignDataSizes(); } fStatus &= OutputItem(TAG_INDEX_END_COLLECTION, 0, 0 ); return (fStatus); } BOOL OutputDataField(PMAIN_ITEM pMI) { BOOL fStatus = TRUE; DWORD dwDataFlags; BYTE bTagIndex; INT GoodBitOffset; INT CurrBitOffset; BOOL InTable; /* // So far, the code path for outputting data items is not significantly // different between regular data fields, arrays, and buffered bytes. // Should they become more independent, probably want to them them up // into their own output routines */ /* // Begin by looking up the current report size/offset for the report ID // and report type. */ InTable = DataTable_LookupReportSize(pMI -> ulReportID, pMI -> ReportType, &CurrBitOffset ); CurrBitOffset &= 7; /* // Here's where the one difference exists. A buffered byte array // must be aligned on Bit 0. Otherwise, we can find the first // good offset to align it on. */ if (FD_BUFFER == pMI -> fType) { GoodBitOffset = 0; } else { /* // Don't need the result of this operation. We already know this // field will align because we won't have gotten this far if it didn't. */ IsAlignmentPossible(CurrBitOffset, pMI -> ulReportSize, pMI -> ulReportCount, &GoodBitOffset ); } if (CurrBitOffset != GoodBitOffset) { fStatus = AlignData(pMI -> ulReportID, pMI -> ReportType, CurrBitOffset, GoodBitOffset ); } if (!fStatus) { return (FALSE); } /* // Output the list of usages */ fStatus &= OutputUsages(pMI -> CommonUsagePage, pMI -> UsageList, pMI -> UsageListLength ); if (pMI -> ulReportID) { fStatus &= OutputItem(TAG_INDEX_REPORT_ID, DetermineULongSize(pMI -> ulReportID), pMI -> ulReportID ); } #pragma warning(disable:4761) fStatus &= OutputItem(TAG_INDEX_LOGICAL_MIN, (pMI -> IsLogMinSigned ? DetermineLongSize(pMI -> lLogicalMinimum) : DetermineULongSize(pMI -> ulLogicalMinimum)), (pMI -> IsLogMinSigned ? (DWORD) pMI -> lLogicalMinimum : (DWORD) pMI -> ulLogicalMinimum) ); fStatus &= OutputItem(TAG_INDEX_LOGICAL_MAX, (pMI -> IsLogMinSigned ? DetermineLongSize(pMI -> lLogicalMaximum) : DetermineULongSize(pMI -> ulLogicalMaximum)), (DWORD) (pMI -> IsLogMinSigned ? pMI -> lLogicalMaximum : pMI -> ulLogicalMaximum) ); #pragma warning(default:4761) fStatus &= OutputItem(TAG_INDEX_REPORT_SIZE, DetermineULongSize(pMI -> ulReportSize), pMI -> ulReportSize ); fStatus &= OutputItem(TAG_INDEX_REPORT_COUNT, DetermineULongSize(pMI -> ulReportCount), pMI -> ulReportCount ); dwDataFlags = CreateDataBitField(pMI); if (HidP_Input == pMI -> ReportType) { bTagIndex = TAG_INDEX_INPUT; } else if (HidP_Output == pMI -> ReportType) { bTagIndex = TAG_INDEX_OUTPUT; } else { bTagIndex = TAG_INDEX_FEATURE; } fStatus &= OutputItem(bTagIndex, DetermineULongSize(dwDataFlags), dwDataFlags ); DataTable_UpdateReportSize( pMI -> ulReportID, pMI -> ReportType, pMI -> ulReportCount * pMI -> ulReportSize ); return (fStatus); } BOOL OutputConstant(PMAIN_ITEM pMI) { BOOL fStatus = TRUE; DWORD dwDataFlags; BYTE bTagIndex; assert (FD_CONSTANT == pMI -> fType); fStatus &= OutputItem(TAG_INDEX_REPORT_SIZE, DetermineULongSize(pMI -> ulReportSize), pMI -> ulReportSize ); fStatus &= OutputItem(TAG_INDEX_REPORT_COUNT, 1, 1 ); if (pMI -> ulReportID) { fStatus &= OutputItem(TAG_INDEX_REPORT_ID, DetermineULongSize(pMI -> ulReportID), pMI -> ulReportID ); } dwDataFlags = ITEM_REPORT_CONSTANT; if (HidP_Input == pMI -> ReportType) { bTagIndex = TAG_INDEX_INPUT; } else if (HidP_Output == pMI -> ReportType) { bTagIndex = TAG_INDEX_OUTPUT; } else { bTagIndex = TAG_INDEX_FEATURE; } DataTable_UpdateReportSize( pMI -> ulReportID, pMI -> ReportType, pMI -> ulReportCount * pMI -> ulReportSize ); fStatus &= OutputItem(bTagIndex, DetermineULongSize(dwDataFlags), dwDataFlags ); return (fStatus); } BOOL OutputItem(BYTE bItemIndex, BYTE bItemSize, DWORD dwItemValue) { /* // This procedure takes the item information that is passed in and attempts // to write it into the current ReportBuffer if it can. */ /* // 1) Determine how much space in the buffer is needed. // 2) If not enough space attempt to resize buffer // 3) If success, copy data into buffer, return (TRUE); // 4) If not success, return (FALSE); */ assert (0 == bItemSize || 1 == bItemSize || 2 == bItemSize || 4 == bItemSize); /* // Set bItemSize = 3 when size is 4 since 11 represents the value for 4 in the item's field // structure */ if ((g_MaxBufferLength - g_CurrBufferLength) < (ULONG) (bItemSize+1)) { if (!ResizeReportBuffer()) { return (FALSE); } } /* // Add the Item tag to the current report buffer and increment the current // report buffer length */ *(g_ReportBuffer + g_CurrBufferLength++) = TagValues[bItemIndex] | ((bItemSize != 4) ? bItemSize : 3); /* // Copy the relevant data in to the report buffer */ memcpy(g_ReportBuffer + g_CurrBufferLength, &dwItemValue, bItemSize); g_CurrBufferLength += bItemSize; return (TRUE); } BOOL AlignData( IN ULONG ReportID, IN HIDP_REPORT_TYPE ReportType, IN ULONG CurrOffset, IN ULONG AlignOffset ) { INT PadSize; PMAIN_ITEM ConstantItem; BOOL Status; PadSize = AlignOffset - CurrOffset + (AlignOffset > CurrOffset ? 0 : 8); ConstantItem = GenerateConstant(PadSize, ReportID, ReportType ); Status = FALSE; if (NULL != ConstantItem) { Status = OutputConstant(ConstantItem); free(ConstantItem); } return (Status); } BOOL AlignDataSizes( VOID ) { BOOL TraverseStatus; BOOL AlignStatus; BOOL IsClosed; ULONG DataSizes[3]; ULONG ReportID; ULONG Index; AlignStatus = TRUE; TraverseStatus = DataTable_GetFirstReportID(&ReportID, &DataSizes[HidP_Input-HidP_Input], &DataSizes[HidP_Output-HidP_Input], &DataSizes[HidP_Feature-HidP_Input], &IsClosed ); while (TraverseStatus) { for (Index = HidP_Input-HidP_Input; Index <= HidP_Feature-HidP_Input; Index++) { if (0 != (DataSizes[Index] % 8)) { AlignStatus &= AlignData(ReportID, Index+HidP_Input, DataSizes[Index] % 8, 0 ); } } TraverseStatus = DataTable_GetNextReportID(&ReportID, &DataSizes[HidP_Input-HidP_Input], &DataSizes[HidP_Output-HidP_Input], &DataSizes[HidP_Feature-HidP_Input], &IsClosed ); } return (AlignStatus); } DWORD CreateDataBitField( IN PMAIN_ITEM pMI ) { DWORD dwDataFlags; dwDataFlags = ITEM_REPORT_DATA; dwDataFlags |= ((FD_ARRAY == pMI -> fType) ? ITEM_REPORT_ARRAY : ITEM_REPORT_VARIABLE); dwDataFlags |= (pMI -> fAbsolute ? ITEM_REPORT_ABSOLUTE : ITEM_REPORT_RELATIVE); dwDataFlags |= (pMI -> fWrap ? ITEM_REPORT_WRAP : ITEM_REPORT_NO_WRAP); dwDataFlags |= (pMI -> fLinear ? ITEM_REPORT_LINEAR : ITEM_REPORT_NONLINEAR); dwDataFlags |= (pMI -> fPreferred ? ITEM_REPORT_PREFERRED : ITEM_REPORT_NOT_PREFERRED); dwDataFlags |= (pMI -> fNull ? ITEM_REPORT_NULL : ITEM_REPORT_NO_NULL); dwDataFlags |= (pMI -> fVolatile ? ITEM_REPORT_VOLATILE : ITEM_REPORT_NONVOLATILE); dwDataFlags |= (pMI -> fBuffered ? ITEM_REPORT_BUFFERED : ITEM_REPORT_BITFIELD); return (dwDataFlags); } BYTE DetermineLongSize(long lValue) { if (lValue >= SCHAR_MIN && lValue <= SCHAR_MAX) { return (1); } else if (lValue >= SHRT_MIN && lValue <= SHRT_MAX) { return (2); } else { return (4); } } BYTE DetermineULongSize(ULONG ulValue) { if (ulValue <= UCHAR_MAX) { return (1); } else if (ulValue <= USHRT_MAX) { return (2); } else { return (4); } } /* // This function determines whether the current ReportSize/ReportCount combination // will correctly align given the BitOffset. I like this problem a lot! // The current implementation will use recursion to verify that all report fields // will not span more than 4-bytes given the current offset. This is more // or less a brute force method. Unfortunately, it is on the order of magnitude // (ReportCount) to currently determine. I think some sort of dynamic programming // technique can get this down to a constant O(8). However, N will never be all THAT // large so even 0(N) when N <= 32 is not bad performance. */ BOOL IsAlignmentProblem( IN INT BitOffset, IN ULONG ReportSize, IN ULONG ReportCount ) { INT NextOffset; /* // Steps to do this... // // 1) Determine if given current offset and report size, will it span more // than four bytes... // 2) If yes, return TRUE // 3) If no, calculate the new bit offset and loop again */ NextOffset = BitOffset; while (ReportCount-- > 0) { if (ReportSize + NextOffset > 32) { return (TRUE); } NextOffset = (NextOffset + ReportSize) % 8; } return (FALSE); } /* // The other interesting part to the above problem is being able to determine // if alignment is even possible...This is actually a relatively simple // problem given we have the above function. */ BOOL IsAlignmentPossible( IN INT BitOffset, IN ULONG ReportSize, IN ULONG ReportCount, OUT INT *GoodOffset ) { INT NextOffset; /* // Since we are given the above function, this problem is not all that difficult... // We'll simply loop through the eight different indices beginning with // BitOffset and call IsAlignmentProblem for each of the given indices. // Since the goal is to minimize the number of bits used in the constant // value, will begin with BitOffset, then BitOffset+1, loop back around until // BitOffset-1 if necesary. Simple eh? */ for (NextOffset = 0; NextOffset < 8; NextOffset++) { if (!IsAlignmentProblem((BitOffset+NextOffset) % 8, ReportSize, ReportCount)) { *GoodOffset = (BitOffset + NextOffset) % 8; return (TRUE); } } return (FALSE); } ULONG SelectUlongNum(ULONG ulMin, ULONG ulMax) { ULONG ulRandNum; ULONG ulSelectRange; assert (ulMax >= ulMin); /* // If we're passed in a ulMin that equals a ulMax, just return that number since this // is the only possible choice. */ if (ulMin == ulMax) { return (ulMin); } ulSelectRange = ulMax - ulMin; if (ULONG_MAX == ulSelectRange) { ulRandNum = rand(); } else { ulRandNum = rand () % (ulSelectRange+1); } return (ulRandNum + ulMin); } long SelectLongNum(long lMin, long lMax) { ULONG ulSelectRange; ULONG ulSelection; ulSelectRange = lMax - lMin; assert (ulSelectRange >= 0); if (ULONG_MAX == ulSelectRange) ulSelection = rand(); else ulSelection = (rand() % (ulSelectRange+1)); return (lMin + ulSelection); } VOID OpenApplCollection( VOID ) { ULONG nClosedIDs; /* // In opening an application, we need to figure exactly how many report // IDs can be used by the given collection. Then the other structures // involved must be initialized so that those report IDs that have be "allocted" // can be tracked. The value of this variable below is irrelevant if // we're not using report IDs. We'll calculate and go to save space and a // comparison */ assert (0 == DataTable_CountOpenReportIDs()); /* // Determine the maximum number of report IDs that can be used for this // given application collection // */ nClosedIDs = DataTable_CountClosedReportIDs(); nMaxAvailCollIDs = nTotalAvailIDs - nClosedIDs - nApplColls; /* // That's all we need to do at this point...We may have to add some functionality // later to track the current collection number. We'll worry about that when we // get to outputting collections and stuff */ return; } VOID CloseApplCollection( VOID ) { BOOL Status; ULONG ReportID; ULONG InputSize; ULONG OutputSize; ULONG FeatureSize; BOOL IsClosed; /* // To close an application collection, all we have to do is loop through // the open report IDs and close them all. Remember, there must // be at least open ID. */ assert (1 <= DataTable_CountOpenReportIDs()); Status = DataTable_GetFirstReportID(&ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); assert (Status); while (Status) { if (!IsClosed) { DataTable_CloseReportID(ReportID); } Status = DataTable_GetNextReportID( &ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); } /* // So far, that's all that needs to be done when closing an application // collection...Again, there may be the need for added functionality // somewhere down the road */ return; } ULONG GetCollectionID( VOID ) { BOOL CreateNewID; ULONG IDNumber; ULONG ReportID; ULONG IDsInUse; BOOL InTable; ULONG InputSize; ULONG OutputSize; ULONG FeatureSize; BOOL IsClosed; /* // This function is to used to determine a ReportID for a given application // collection when a field generator makes a request for a new report ID. // It must perform the following steps. // // 1) Check to see if we are using report IDs. If we're not, then return // 0, the default report ID // // 2) If we are, we will determine whether to generate a new report ID or // whether to use a currently open one. To start with, we'll use // a 50/50 determination, however, this might want to be changed in // the future to give a better distribution. There's a good chance // with deep collection that we may take up the majority of available // IDs with the first application collection and leave only 1 apiece // for subsequent collections. // // 3) If using a new report ID, generate a random number between 1 and 255 // because report IDs are only one byte long. If that number is currently // in the table, oh well, we'll use it anyway, just don't decrement the // number of available IDs then. // // 4) If using an existing ID, choose one from the open IDs stored in the data // table and return it. // // Not too hard, eh? */ if (!fUseReportIDs) { DataTable_AddReportID(0); return (0); } IDsInUse = DataTable_CountOpenReportIDs(); if (0 == IDsInUse) { CreateNewID = TRUE; } else { CreateNewID = SelectOption(FALSE, (IDsInUse != nMaxAvailCollIDs)); } if (CreateNewID) { while (1) { ReportID = SelectUlongNum(1, 255); InTable = DataTable_LookupReportID(ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); /* // If it's not in the table, we need to increment the // number of IDs */ if (!InTable || (InTable && !IsClosed)) { break; } } DataTable_AddReportID(ReportID); } else { IDNumber = SelectUlongNum(1, IDsInUse); InTable = DataTable_GetFirstReportID(&ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); while (1) { assert (InTable); if (!IsClosed) { if (0 == --IDNumber) { break; } } InTable = DataTable_GetNextReportID(&ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); } } return (ReportID); } BOOL GetUsages( IN ULONG UsageCount, IN ULONG BitLimit, IN BOOL UseExtendedUsages, OUT PUSAGE CommonUsagePage, OUT PULONG UsageListLength, OUT PUSAGE_LIST *UsageList ) { USAGE UsageMin; USAGE UsageMax; ULONG UsagesGenerated; BOOL AreDups; BOOL UsingCommonPage; USAGE_LIST CurrUsage; PUSAGE_LIST MasterList; PUSAGE_LIST NewList; ULONG MaxListLength; ULONG MasterListLength; BOOL Status; /* // To define our list of usages we need to do the following steps // until we have defined all the necessary usages. But first, // a description of the parameters. // // UsageCount -- The number of usages we need to come up with // BitLimit -- Any limits on the max usage value we can use do // to a small field size, (ie. arrays report // actual usage values, if each array slot is only // seven bits, the values this function generates // cannot be more than seven bits // // UsageList -- This is a pointer to a list allocated by this routine // which describes all the usages that have been generate // see the USAGE_LIST structure // */ /* // Here's how this usage thing will be done: // // 1) Determine our max/min range for usages (1..(2^BitLimit)-1)) // BitLimit should never be more than 16 bits // // 2) If UsageCount is 1, we generate a simple usage (not a range) // 3) If UsageCount > 1, we'll determine by some algorithm whether // to create a Usage range or a simple Usage // 4) In generating a UsagePage, two things will be done. First, the // CommonUsagePage is chosen, all other usages in the list will // either use the CommonUsagePage or generate their own usage page // 5) Next generate a UsagePage to be used by either the range or // the simple usage // 6) If simple Usage, pick a usage within the bounds establish in 1 // check to see if the UsagePage/Usage combo has already been used // If so, go back to step 2 // // 7) If Usage range is to be generated, determine first how big the // range will be (2..UsageCount). Generate the range. See if any // usage in this range has been generated already in this list of // usages. If not, it's a good range. If yes, go back to step two // and try again. // 8) We've generated one set of usages, determine how many usages were // generated and decrement usage count by that amount. If usage // count is 0, we can bolt. If not repeat step 2 until 0. */ /* // NOTE: We may have to look into tracking Usages usage for each RepID/ReportType // combo as well. This is really becoming annoying. */ assert (1 <= UsageCount); assert (sizeof(USAGE)*8 >= BitLimit && 1 < BitLimit); /* // These may get set later to deal with possible usage limits set in the user // options. We'll get this up and running first however, */ UsageMin = (USAGE) OPTIONS_USAGE_MIN; UsageMax = ((ULONG) ((1 << BitLimit)-1) < OPTIONS_USAGE_MAX ? (USAGE) ((1 << BitLimit)-1) : (USAGE) OPTIONS_USAGE_MAX); /* // Cannot have our usage range be less than our UsageCount */ assert((ULONG) (UsageMax - UsageMin + 1) >= UsageCount); /* // Try to initialize the master list. Initially start at four or UsageCount // whichever is smaller. */ MaxListLength = (UsageCount < 4) ? UsageCount : 4; MasterList = (PUSAGE_LIST) calloc(MaxListLength, sizeof(USAGE_LIST)); MasterListLength = 0; if (NULL == MasterList) { *UsageList = NULL; *UsageListLength = 0; return (FALSE); } /* // Before generating all the usages we need, we need to create the // CommonUsagePage. */ *CommonUsagePage = (USAGE) SelectUlongNum(OPTIONS_USAGEPAGE_MIN, OPTIONS_USAGEPAGE_MAX); UsingCommonPage = FALSE; /* // Loop until we have found all the usages we need */ Status = TRUE; while (UsageCount > 0) { /* // We can only possibly create a range when the usage count is greater // than one. If 1 == UsageCount, the second parameter to // SelectOption will be false and will get false back. */ CurrUsage.IsMinMax = SelectOption(FALSE, 1 < UsageCount); assert (1 == UsageCount ? !CurrUsage.IsMinMax : TRUE); /* // Select the UsagePage to use for this set of usages. We'll use either // the CommonUsagePage or an extended usage page if UseEx */ if (SelectOption(FALSE, UseExtendedUsages)) { CurrUsage.UsagePage = (USAGE) SelectUlongNum(OPTIONS_USAGEPAGE_MIN, OPTIONS_USAGEPAGE_MAX); } else { CurrUsage.UsagePage = *CommonUsagePage; UsingCommonPage = TRUE; } /* // Now select our usages */ if (CurrUsage.IsMinMax) { /* // Determine our range here */ UsagesGenerated = SelectUlongNum(2, UsageCount); CurrUsage.UsageMin = (USAGE) SelectUlongNum(UsageMin, UsageMax-UsagesGenerated+1); CurrUsage.UsageMax = (USAGE) CurrUsage.UsageMin + (USAGE) UsagesGenerated - 1; } else { CurrUsage.Usage = (USAGE) SelectUlongNum(UsageMin, UsageMax); UsagesGenerated = 1; } /* // Now we're going to need to check for duplicate usages // If there are no duplicates, then we decrement the usage count // and loop again */ AreDups = SearchForUsageDups(MasterList, MasterListLength, &CurrUsage); if (!AreDups) { UsageCount -= UsagesGenerated; /* // If we've extended the bounds of our usage list, we need // to resize the darn thing. If we fail on resizing we must // break out of our loop and return FALSE */ if (MasterListLength == MaxListLength) { NewList = (PUSAGE_LIST) realloc(MasterList, MaxListLength*2*sizeof(USAGE_LIST)); if (NULL == NewList) { free(MasterList); MasterList = NULL; MasterListLength = 0; Status = FALSE; break; } MaxListLength *= 2; MasterList = NewList; } *(MasterList+MasterListLength++) = CurrUsage; } } /* // If we don't actually use the CommonUsagePage, we may decide to clear the // page or not. In theory, even if all extended usages are used, the parse // should not choke when there is a common page. If the CommonUsagePage // is not 0, then usage generation routine should output that usage page // first when outputting a data field. */ if (!UsingCommonPage) { *CommonUsagePage = (SelectOption(FALSE, TRUE) ? 0 : *CommonUsagePage); } *UsageList = MasterList; *UsageListLength = MasterListLength; return (TRUE); } BOOL SearchForUsageDups( IN PUSAGE_LIST MasterList, IN ULONG MasterListLength, IN PUSAGE_LIST Usage ) { /* // To search for duplicates in the list we need to search through // every USAGE_LIST structure in the list and determine if the // passed in Usage overlaps. This is relatively quite simple to // do. // // 1) Check to see if the usage pages match. // 2) If no, move on. // 3) If yes, if Usage is a single usage see if it either falls in // the range of the current structure or is equal to the usage // of the current structure depending on whether the structure // is a range or not. // // 4) If yes and Usage is range, need to make sure that two ranges // don't overlapped or that a single usage doesn't fall within the // range. */ ULONG Index; PUSAGE_LIST CurrUsage; BOOL Status; Status = FALSE; for (CurrUsage = MasterList, Index = 0; Index < MasterListLength; Index++, CurrUsage++) { if (CurrUsage -> UsagePage == Usage -> UsagePage) { if (!Usage -> IsMinMax) { if (!CurrUsage -> IsMinMax) { if (CurrUsage -> Usage == Usage -> Usage) { Status = TRUE; break; } } else { if (IN_USAGE_RANGE(Usage -> Usage, CurrUsage -> UsageMin, CurrUsage -> UsageMax)) { Status = TRUE; break; } } } else { if (!CurrUsage -> IsMinMax) { if (IN_USAGE_RANGE(CurrUsage -> Usage, Usage -> UsageMin, Usage -> UsageMax)) { Status = TRUE; break; } } else { if (IN_USAGE_RANGE(CurrUsage -> UsageMin, Usage -> UsageMin, Usage -> UsageMax) || IN_USAGE_RANGE(CurrUsage -> UsageMax, Usage -> UsageMin, Usage -> UsageMax) || IN_USAGE_RANGE(Usage -> UsageMin, CurrUsage -> UsageMin, CurrUsage -> UsageMax) || IN_USAGE_RANGE(Usage -> UsageMax, CurrUsage -> UsageMin, CurrUsage -> UsageMax)) { Status = TRUE; break; } } } } } return (Status); } BOOL InitializeReportBuffer( VOID ) { g_ReportBuffer = (PUCHAR) malloc(BUFFER_INIT_SIZE); g_CurrBufferLength = 0; g_MaxBufferLength = ((NULL != g_ReportBuffer) ? BUFFER_INIT_SIZE : 0); return (NULL != g_ReportBuffer); } BOOL ResizeReportBuffer( VOID ) { PUCHAR OldBuffer; OldBuffer = g_ReportBuffer; g_ReportBuffer = (PUCHAR) realloc(OldBuffer, g_MaxBufferLength+BUFFER_INCREMENT_SIZE); if (NULL == g_ReportBuffer) { g_ReportBuffer = OldBuffer; return (FALSE); } g_MaxBufferLength += BUFFER_INCREMENT_SIZE; return (TRUE); } VOID CountLinkCollNodes( IN PMAIN_ITEM Collection, OUT PUSHORT NumLinkCollNodes ) { USHORT LinkNodes; USHORT TempNodes; ULONG Index; PMAIN_ITEM CurrItem; LinkNodes = 1; for (Index = 0; Index < Collection -> ulNumCompositeItems; Index++) { CurrItem = Collection -> ComposedItems[Index]; if (MI_COLLECTION == CurrItem -> miType) { CountLinkCollNodes(CurrItem, &TempNodes ); LinkNodes += TempNodes; } } *NumLinkCollNodes = LinkNodes; return; } VOID CountValueAndButtonCaps( IN PMAIN_ITEM Collection, IN HIDP_REPORT_TYPE ReportType, OUT PUSHORT NumValueCaps, OUT PUSHORT NumButtonCaps ) { USHORT ValueCount; USHORT ButtonCount; USHORT TempValueCount; USHORT TempButtonCount; ULONG Index; ULONG i; ULONG ReportCount; PMAIN_ITEM CurrItem; assert (MI_COLLECTION == Collection -> miType); ValueCount = 0; ButtonCount = 0; for (Index = 0; Index < Collection -> ulNumCompositeItems; Index++) { CurrItem = Collection -> ComposedItems[Index]; if (MI_COLLECTION == CurrItem -> miType) { CountValueAndButtonCaps(CurrItem, ReportType, &TempValueCount, &TempButtonCount ); ValueCount += TempValueCount; ButtonCount += TempButtonCount; } else if (ReportType == CurrItem -> ReportType) { switch (CurrItem -> fType) { case FD_ARRAY: case FD_BUTTONS: ButtonCount += (USHORT) CurrItem -> UsageListLength; break; case FD_BUFFER: case FD_DATA: /* // To correctly determine the number of value caps for a given // main item, the ReportCount of the given main item along // with the UsageList has to be examined. We'll use // the following algorithm for counting value caps // // 1) Loop through the UsageList until we hit no more usages // a) If not a range usage, add 1 to our value cap count // Decrement ReportCount by 1 // // b) If a range usage, add 1 to the value cap count // and decrement report count by the number of usages // in the range // // 2) If there are no more usages left but the ReportCount > 0, need // to take the last usage. If that last usage is not a range, // we're done as that usage will actually be included with the // last value cap. // // In theory, if it is a range, however, we take the usage // maximum and assume another usage of ReportCount with that maximum. // However, that is not the HIDPARSE implementation and it won't // get fixed. To meet the HIDPARSE implementation, we need to // just ignore these unusaged values. // // NOTE: The above algorithm actually shows a little more detail than // is necessary for counting value caps but we this will be // the basic format once the actual structures start getting // generated as well. */ ReportCount = CurrItem -> ulReportCount; for (i = 0; i < CurrItem -> UsageListLength; i++) { if (CurrItem -> UsageList[i].IsMinMax) { ValueCount++; ReportCount -= (CurrItem -> UsageList[i].UsageMax - CurrItem -> UsageList[i].UsageMin + 1); } else { ValueCount++; ReportCount--; } } break; } } } *NumValueCaps = ValueCount; *NumButtonCaps = ButtonCount; return; } BOOL BuildReportSizes( IN PMAIN_ITEM Collection ) { BOOL Status; PMAIN_ITEM CurrItem; ULONG Index; ULONG ReportSize; BOOL CanAlign; ULONG AlignOffset; LONG PadSize; assert (MI_COLLECTION == Collection -> miType); Status = TRUE; for (Index = 0; (Index < Collection -> ulNumCompositeItems && Status); Index++) { CurrItem = Collection -> ComposedItems[Index]; if (MI_COLLECTION == CurrItem -> miType) { Status = BuildReportSizes(CurrItem); } else { /* // Determine if we need to perform some alignment on this report // item. */ (VOID) DataTable_LookupReportSize(CurrItem -> ulReportID, CurrItem -> ReportType, &ReportSize ); /* // Don't really care about the size, just the offset so we'll MOD by // 8 which is bitwise & with 7 */ ReportSize &= 7; /* // Call the IsAlignmentPossible function. This should always return // TRUE but it will also determine if we need to put in some padding // bits to this thing as well. */ CanAlign = IsAlignmentPossible(ReportSize, CurrItem -> ulReportSize, CurrItem -> ulReportCount, &AlignOffset ); assert (CanAlign); PadSize = AlignOffset - ReportSize; if (PadSize < 0) PadSize += 8; /* // PadSize should be somewhere between 0 and 7 at this point */ assert (0 <= PadSize && 8 > PadSize); /* // Update the report size if padding of this report will need to be done */ Status = DataTable_UpdateReportSize(CurrItem -> ulReportID, CurrItem -> ReportType, (ULONG) PadSize ); /* // Then, add the actual report size to the list as well. */ if (Status) { Status = DataTable_UpdateReportSize(CurrItem -> ulReportID, CurrItem -> ReportType, CurrItem -> ulReportSize * CurrItem -> ulReportCount ); } } } return (Status); } BOOL CalcReportLengths( IN PMAIN_ITEM Collection, OUT PUSHORT InputReportLength, OUT PUSHORT OutputReportLength, OUT PUSHORT FeatureReportLength ) { ULONG InputSize; ULONG OutputSize; ULONG FeatureSize; ULONG ReportID; ULONG ByteOffset; BOOL IDFound; BOOL IsClosed; BOOL Status; /* // To determine the maximum report size, we need to take in to account the // following items: // 1) If there is no items for the given report type then the length is // zero, otherwise the minimum is 2. There can be no 1 byte buffer since // if a data item exists, there is a minimum of one byte for the data value // and one byte for the required report ID (even if it might be zero) // // 2) Must consider the alignment of data structures. Therefore, this routine // will take advantage of the DataTable routines that are used in // generating the report descriptor and used in outputting the report // descriptor to track the current alignment situation. */ /* // Initialize the data table and the output parameters */ DataTable_InitTable(); *InputReportLength = 0; *OutputReportLength = 0; *FeatureReportLength = 0; /* // The whole process is relatively simple. We need to traverse all the items // under the given TopLevelCollection and update the field for the given // ReportID/ReportType combo if it is a data item. If it's not a data // item, we need to traverse the given collection. This will use a recursive // function to easily traverse this structure. Once this function // has completed it's task, we'll query all the ReportIDs in the datatable // and look for the greatest of the reportIDs for Input/Output/Feature reports */ Status = BuildReportSizes(Collection); if (Status) { /* // Retrieve all the ReportIDs stored in the data structure */ IDFound = DataTable_GetFirstReportID(&ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); while (IDFound) { *InputReportLength = InputSize > *InputReportLength ? (USHORT) InputSize : *InputReportLength; *OutputReportLength = OutputSize > *OutputReportLength ? (USHORT) OutputSize : *OutputReportLength; *FeatureReportLength = FeatureSize > *FeatureReportLength ? (USHORT) FeatureSize : *FeatureReportLength; IDFound = DataTable_GetNextReportID(&ReportID, &InputSize, &OutputSize, &FeatureSize, &IsClosed ); } } /* // The data that was retrieved is stored in bits for the report, not bytes. // Now we need to look at each of the report lengths and round up to the // nearest byte */ ByteOffset = *InputReportLength & 7; *InputReportLength = (*InputReportLength >> 3) + (ByteOffset ? 1 : 0); ByteOffset = *OutputReportLength & 7; *OutputReportLength = (*OutputReportLength >> 3) + (ByteOffset ? 1 : 0); ByteOffset = *FeatureReportLength & 7; *FeatureReportLength = (*FeatureReportLength >> 3) + (ByteOffset ? 1 : 0); /* // If the length that was determined to store the data is > 0, then increment // the size by one to make room for the reportID. */ if (*InputReportLength) (*InputReportLength)++; if (*OutputReportLength) (*OutputReportLength)++; if (*FeatureReportLength) (*FeatureReportLength)++; /* // Destroy the data table and return the status we received from the // BuildReportSize routine */ DataTable_DestroyTable(); return (Status); } VOID FillLinkNodes( IN PMAIN_ITEM Collection, IN PHIDP_LINK_COLLECTION_NODE NodeList, IN USHORT FirstNodeIndex, IN USHORT ParentIndex, OUT PUSHORT NumFilled ) { PHIDP_LINK_COLLECTION_NODE CurrNode; PMAIN_ITEM CurrItem; USHORT NextNodeIndex; USHORT nFilled; ULONG Index; /* // To fill in the node structure involves the following steps // // 1) Initialize the first node in the index to the initial value // 2) Loop through the number of composed items in the collection // and recursively call itself to fill in the nodes for each // collection in the list // // 3) Update the values of the current collection based on what was // filled in by the recursive call */ CurrNode = NodeList; /* // Step 1: Initialization of node */ CurrNode -> LinkUsagePage = Collection -> UsageList -> UsagePage; CurrNode -> LinkUsage = Collection -> UsageList -> Usage; CurrNode -> Parent = ParentIndex; CurrNode -> NumberOfChildren = 0; CurrNode -> NextSibling = 0; CurrNode -> FirstChild = 0; /* // Initialization of local variables */ NextNodeIndex = 1; /* // Step 2: Loop through all the composed items of the current collection */ for (Index = 0; Index < Collection -> ulNumCompositeItems; Index++) { CurrItem = Collection -> ComposedItems[Index]; if (IS_COLLECTION(CurrItem)) { FillLinkNodes( CurrItem, NodeList+NextNodeIndex, (USHORT) (FirstNodeIndex+NextNodeIndex), FirstNodeIndex, &nFilled ); /* // Step 3: Update the local variables based on what was found // filled in by the previous call. We need to update // the following three things. // a) The next sibling of the node that just got filled in // b) The first child of the current node. // c) The next node in our list */ (NodeList+NextNodeIndex) -> NextSibling = CurrNode -> FirstChild; CurrNode -> FirstChild = NextNodeIndex+FirstNodeIndex; NextNodeIndex += (USHORT) nFilled; CurrNode -> NumberOfChildren++; } } *NumFilled = NextNodeIndex; return; } VOID FillButtonCaps( IN PMAIN_ITEM Collection, IN PUSHORT LinkCollNum, IN HIDP_REPORT_TYPE ReportType, IN PHIDP_BUTTON_CAPS ButtonCapsList, OUT PUSHORT NumFilled ) { PMAIN_ITEM CurrItem; USHORT nFilled; ULONG Index; ULONG UsageIndex; PHIDP_BUTTON_CAPS CurrCaps; USHORT CapsAdded; PUSAGE_LIST CurrUsage; USHORT CurrCollNum; /* // Filling the button caps is relatively easy. For each non-collection // in the collection, the */ CurrCaps = ButtonCapsList; CapsAdded = 0; CurrCollNum = *LinkCollNum; for (Index = 0; Index < Collection -> ulNumCompositeItems; Index++) { CurrItem = Collection -> ComposedItems[Index]; if (IS_COLLECTION(CurrItem)) { (*LinkCollNum)++; FillButtonCaps(CurrItem, LinkCollNum, ReportType, CurrCaps, &nFilled ); CapsAdded += nFilled; CurrCaps += nFilled; } else { if (CurrItem -> ReportType == ReportType && IS_BUTTON_ITEM(CurrItem)) { CurrUsage = CurrItem -> UsageList; for (UsageIndex = 0; UsageIndex < CurrItem -> UsageListLength; UsageIndex++, CurrUsage++, CurrCaps++) { if (0 == CurrUsage -> UsagePage) { CurrCaps -> UsagePage = CurrItem -> CommonUsagePage; } else { CurrCaps -> UsagePage = CurrUsage -> UsagePage; } CurrCaps -> ReportID = (UCHAR) CurrItem -> ulReportID; CurrCaps -> BitField = (USHORT) CreateDataBitField(CurrItem); CurrCaps -> LinkCollection = CurrCollNum; CurrCaps -> LinkUsage = Collection -> UsageList -> Usage; CurrCaps -> LinkUsagePage = Collection -> UsageList -> UsagePage; CurrCaps -> IsAbsolute = CurrItem -> fAbsolute; CurrCaps -> IsRange = CurrUsage -> IsMinMax; CurrCaps -> IsStringRange = FALSE; CurrCaps -> IsDesignatorRange = FALSE; if (CurrCaps -> IsRange) { CurrCaps -> Range.UsageMin = CurrUsage -> UsageMin; CurrCaps -> Range.UsageMax = CurrUsage -> UsageMax; CurrCaps -> Range.StringMin = 0; CurrCaps -> Range.StringMax = 0; CurrCaps -> Range.DesignatorMin = 0; CurrCaps -> Range.DesignatorMax = 0; } else { CurrCaps -> NotRange.Usage = CurrUsage -> Usage; CurrCaps -> NotRange.StringIndex = 0; CurrCaps -> NotRange.DesignatorIndex = 0; } CapsAdded++; } } } } *NumFilled = CapsAdded; return; } VOID FillValueCaps( IN PMAIN_ITEM Collection, IN USHORT LinkCollNum, IN HIDP_REPORT_TYPE ReportType, IN PHIDP_VALUE_CAPS ValueCapsList, OUT PUSHORT NumFilled ) { PMAIN_ITEM CurrItem; USHORT nFilled; ULONG Index; PHIDP_VALUE_CAPS CurrCaps; USHORT CapsAdded; ULONG ReportCount; ULONG i; CurrCaps = ValueCapsList; CapsAdded = 0; for (Index = 0; Index < Collection -> ulNumCompositeItems; Index++) { CurrItem = Collection -> ComposedItems[Index]; if (IS_COLLECTION(CurrItem)) { FillValueCaps(CurrItem, (USHORT) (LinkCollNum+1), ReportType, CurrCaps, &nFilled ); LinkCollNum++; CapsAdded += nFilled; CurrCaps += nFilled; } else { /* // See note in CountValueAndButtonCaps on how the value caps structures // will be determined based on the given data item */ /* // If this item matches the report type we are filling in caps for // add this item to our list of Caps structures */ if (CurrItem -> ReportType == ReportType && IS_VALUE_ITEM(CurrItem)) { ReportCount = CurrItem -> ulReportCount; for (i = 0; i < CurrItem -> UsageListLength; i++, CurrCaps++) { if (0 == CurrItem -> UsageList[i].UsagePage) { CurrCaps -> UsagePage = CurrItem -> CommonUsagePage; } else { CurrCaps -> UsagePage = CurrItem -> UsageList[i].UsagePage; } CurrCaps -> ReportID = (UCHAR) CurrItem -> ulReportID; CurrCaps -> BitField = (USHORT) CreateDataBitField(CurrItem); CurrCaps -> LinkCollection = LinkCollNum; CurrCaps -> LinkUsage = Collection -> UsageList -> Usage; CurrCaps -> LinkUsagePage = Collection -> UsageList -> UsagePage; CurrCaps -> IsRange = CurrItem -> UsageList[i].IsMinMax; CurrCaps -> IsStringRange = FALSE; CurrCaps -> IsDesignatorRange = FALSE; CurrCaps -> IsAbsolute = CurrItem -> fAbsolute; /* // NOTE: Currently we must set HasNull to 0x40 instead // of one due to a bug in HIDParse */ CurrCaps -> HasNull = CurrItem -> fNull ? 0x40 : 0; CurrCaps -> BitSize = (USHORT) CurrItem -> ulReportSize; CurrCaps -> LogicalMin = CurrItem -> lLogicalMinimum; CurrCaps -> LogicalMax = CurrItem -> lLogicalMaximum; if (CurrCaps -> IsRange) { CurrCaps -> Range.UsageMin = CurrItem -> UsageList[i].UsageMin; CurrCaps -> Range.UsageMax = CurrItem -> UsageList[i].UsageMax; CurrCaps -> Range.StringMin = 0; CurrCaps -> Range.StringMax = 0; CurrCaps -> Range.DesignatorMin = 0; CurrCaps -> Range.DesignatorMax = 0; CurrCaps -> ReportCount = 1; ReportCount -= (CurrCaps -> Range.UsageMax - CurrCaps -> Range.UsageMin + 1); } else { CurrCaps -> NotRange.Usage = CurrItem -> UsageList[i].Usage; CurrCaps -> NotRange.StringIndex = 0; CurrCaps -> NotRange.DesignatorIndex = 0; CurrCaps -> ReportCount = 1; ReportCount--; } CapsAdded++; } /* // If we've run out of usages but still have a values that haven't // been counted yet. In theory, iff the last usagelist value was a range // then we need to add a new value caps with the usage max as // the Usage of the new caps and the ReportCount value will be // be the ReportCount value for that new caps. However, the HIDPARSE // implementation doesn't do this and just leaves the previous value caps // and basically the other values cannot be touched. Since this implementation // won't be fixed, we must compensate for it and do as it does. // // If the last UsageList value was not a range, we simply need // to increment the ReportCount value of the last caps structure // we dealt with */ if (ReportCount > 0) { if (!CurrItem -> UsageList[i-1].IsMinMax) { (CurrCaps-1) -> ReportCount += (USHORT) ReportCount; } } } } } *NumFilled = CapsAdded; return; }