695 lines
16 KiB
C
695 lines
16 KiB
C
#define __DATATBL_C__
|
|
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <wtypes.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
#include <hidusage.h>
|
|
#include <hidsdi.h>
|
|
#include <hidpi.h>
|
|
#include <bingen.h>
|
|
#include "datatbl.h"
|
|
|
|
|
|
/*
|
|
// This file contains all the implementation details for the data size table
|
|
// structure.
|
|
*/
|
|
|
|
/*
|
|
// Define the maximum size for the data hash table
|
|
*/
|
|
|
|
#define DATA_TABLE_SIZE 8
|
|
|
|
/*
|
|
// Define the hashing function to be used to determine the index:
|
|
// Very simplistic for now...Can deal with coming up with a better
|
|
// hash function when I have time and the appropriate references
|
|
*/
|
|
|
|
#define HASH(rid) ((rid) % DATA_TABLE_SIZE)
|
|
|
|
/*
|
|
// Typedef the structure that is used in the hash tables chaining branches
|
|
*/
|
|
|
|
typedef struct _datachain {
|
|
LIST_ENTRY ListEntry;
|
|
|
|
ULONG ReportID;
|
|
ULONG InputSize;
|
|
ULONG OutputSize;
|
|
ULONG FeatureSize;
|
|
BOOL IsClosed;
|
|
|
|
} DATA_TABLE_ENTRY, *PDATA_TABLE_ENTRY;
|
|
|
|
#define FORWARD_ENTRY(Curr) ((PDATA_TABLE_ENTRY) (Curr) -> ListEntry.Flink)
|
|
#define BACKWARD_ENTRY(Curr) ((PDATA_TABLE_ENTRY) (Curr) -> ListEntry.Blink)
|
|
|
|
#define FREE_ENTRY(Entry) \
|
|
{ \
|
|
assert(NULL != (Entry)); \
|
|
free((Entry)); \
|
|
}
|
|
|
|
#define ALLOC_ENTRY() (malloc(sizeof(DATA_TABLE_ENTRY)))
|
|
|
|
/*
|
|
// Declare the data table structure
|
|
*/
|
|
|
|
static PDATA_TABLE_ENTRY DataSizeTable[DATA_TABLE_SIZE];
|
|
|
|
/*
|
|
// Variables to deal with traversing through the table
|
|
*/
|
|
|
|
static ULONG TraverseIndex;
|
|
static PDATA_TABLE_ENTRY TraverseEntry;
|
|
static BOOL TraverseEnd = TRUE;
|
|
|
|
static BOOL UseBP = FALSE;
|
|
/*
|
|
// Declare the prototypes for any local functions.
|
|
*/
|
|
|
|
BOOL
|
|
DataTable_SearchHashChain(
|
|
IN PDATA_TABLE_ENTRY Chain,
|
|
IN ULONG ReportID,
|
|
OUT PDATA_TABLE_ENTRY *Entry
|
|
);
|
|
|
|
BOOL
|
|
DataTable_SearchDataTable(
|
|
IN ULONG ReportID,
|
|
OUT PULONG Index,
|
|
OUT PDATA_TABLE_ENTRY *Entry
|
|
);
|
|
|
|
PDATA_TABLE_ENTRY
|
|
DataTable_AddNewEntry(
|
|
IN ULONG ReportID
|
|
);
|
|
|
|
/*
|
|
// Definitions of the publically available functions
|
|
*/
|
|
|
|
VOID
|
|
DataTable_InitTable(
|
|
VOID
|
|
)
|
|
{
|
|
LONG Index;
|
|
|
|
/*
|
|
// Using Index as a for loop, let's make sure we don't ever overrun
|
|
// our for loop bounds -- catch the error now not later...In reality
|
|
// this check is useless since a hash table of LONG_MAX entries is
|
|
// ridiculous especially given this implementation (255 report IDS *
|
|
// 3 report types) << LONG_MAX) but it's a good habit to follow.
|
|
*/
|
|
|
|
assert (DATA_TABLE_SIZE < LONG_MAX);
|
|
|
|
/*
|
|
// Simply initialize all the pointers to NULL
|
|
*/
|
|
|
|
for (Index = 0; Index < DATA_TABLE_SIZE; Index++) {
|
|
|
|
DataSizeTable[Index] = NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
DataTable_UpdateReportSize(
|
|
IN ULONG ReportID,
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN ULONG AddedSize
|
|
)
|
|
{
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
LONG Index;
|
|
BOOL InTable;
|
|
|
|
/*
|
|
// Validate the two parameters (reportID and ReportType) using asserts
|
|
*/
|
|
|
|
assert(256 > ReportID);
|
|
assert(HidP_Input <= ReportType && HidP_Feature >= ReportType);
|
|
|
|
/*
|
|
// Get the index for our hash table
|
|
*/
|
|
|
|
InTable = DataTable_SearchDataTable(ReportID,
|
|
&Index,
|
|
&CurrEntry
|
|
);
|
|
|
|
|
|
if (!InTable) {
|
|
|
|
CurrEntry = DataTable_AddNewEntry(ReportID);
|
|
|
|
if (NULL == CurrEntry) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
switch (ReportType) {
|
|
case HidP_Input:
|
|
CurrEntry -> InputSize += AddedSize;
|
|
break;
|
|
|
|
case HidP_Output:
|
|
CurrEntry -> OutputSize += AddedSize;
|
|
break;
|
|
|
|
case HidP_Feature:
|
|
CurrEntry -> FeatureSize += AddedSize;
|
|
break;
|
|
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
BOOL
|
|
DataTable_UpdateAllReportSizes(
|
|
IN ULONG ReportID,
|
|
IN ULONG InputSize,
|
|
IN ULONG OutputSize,
|
|
IN ULONG FeatureSize
|
|
)
|
|
{
|
|
BOOL ReturnStatus;
|
|
|
|
ReturnStatus = DataTable_UpdateReportSize(ReportID, HidP_Input, InputSize);
|
|
|
|
/*
|
|
// If we have a true returnStatus, in theory the next two calls should complete
|
|
// because the only time ReturnStatus would be FALSE is if the UpdateDataSize
|
|
// had to create a new node and couldn't due to memory considerations.
|
|
// Therefore, we should be able to just ignore the return value of
|
|
// subsequent UpdateDataSize calls.
|
|
*/
|
|
|
|
if (ReturnStatus) {
|
|
DataTable_UpdateReportSize(ReportID, HidP_Output, OutputSize);
|
|
DataTable_UpdateReportSize(ReportID, HidP_Feature, FeatureSize);
|
|
}
|
|
|
|
return (ReturnStatus);
|
|
}
|
|
|
|
BOOL
|
|
DataTable_LookupReportSize(
|
|
IN ULONG ReportID,
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
OUT PULONG ReportSize
|
|
)
|
|
{
|
|
BOOL ReturnStatus;
|
|
|
|
ULONG InputSize;
|
|
ULONG OutputSize;
|
|
ULONG FeatureSize;
|
|
|
|
BOOL IsClosed;
|
|
|
|
ReturnStatus = DataTable_LookupReportID(ReportID,
|
|
&InputSize,
|
|
&OutputSize,
|
|
&FeatureSize,
|
|
&IsClosed
|
|
);
|
|
|
|
if (ReturnStatus) {
|
|
|
|
switch (ReportType) {
|
|
case HidP_Input:
|
|
*ReportSize = InputSize;
|
|
break;
|
|
|
|
case HidP_Output:
|
|
*ReportSize = OutputSize;
|
|
break;
|
|
|
|
|
|
case HidP_Feature:
|
|
*ReportSize = FeatureSize;
|
|
break;
|
|
|
|
|
|
default:
|
|
assert(0);
|
|
|
|
}
|
|
}
|
|
else {
|
|
*ReportSize = 0;
|
|
}
|
|
|
|
return (ReturnStatus);
|
|
}
|
|
|
|
BOOL
|
|
DataTable_LookupReportID(
|
|
IN ULONG ReportID,
|
|
OUT PULONG InputSize,
|
|
OUT PULONG OutputSize,
|
|
OUT PULONG FeatureSize,
|
|
OUT PBOOL IsClosed
|
|
)
|
|
{
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
ULONG Index;
|
|
BOOL InTable;
|
|
|
|
/*
|
|
// Validate the two parameters (reportID and ReportType) using asserts
|
|
*/
|
|
|
|
assert(256 > ReportID);
|
|
|
|
InTable = DataTable_SearchDataTable(ReportID,
|
|
&Index,
|
|
&CurrEntry
|
|
);
|
|
|
|
if (InTable) {
|
|
*InputSize = CurrEntry -> InputSize;
|
|
*OutputSize = CurrEntry -> OutputSize;
|
|
*FeatureSize = CurrEntry -> FeatureSize;
|
|
*IsClosed = CurrEntry -> IsClosed;
|
|
}
|
|
|
|
return (InTable);
|
|
}
|
|
|
|
BOOL
|
|
DataTable_AddReportID(
|
|
IN ULONG ReportID
|
|
)
|
|
{
|
|
BOOL InTable;
|
|
ULONG Index;
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
|
|
InTable = DataTable_SearchDataTable(ReportID,
|
|
&Index,
|
|
&CurrEntry
|
|
);
|
|
|
|
if (InTable) {
|
|
return (TRUE);
|
|
}
|
|
|
|
CurrEntry = DataTable_AddNewEntry(ReportID);
|
|
|
|
return (NULL != CurrEntry);
|
|
|
|
}
|
|
|
|
ULONG
|
|
DataTable_CountOpenReportIDs(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG Index;
|
|
ULONG OpenCount;
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
|
|
OpenCount = 0;
|
|
for (Index = 0; Index < DATA_TABLE_SIZE; Index++) {
|
|
|
|
CurrEntry = DataSizeTable[Index];
|
|
|
|
while (NULL != CurrEntry) {
|
|
|
|
if (!CurrEntry -> IsClosed) {
|
|
OpenCount++;
|
|
}
|
|
|
|
CurrEntry = FORWARD_ENTRY(CurrEntry);
|
|
}
|
|
}
|
|
|
|
return (OpenCount);
|
|
}
|
|
|
|
ULONG
|
|
DataTable_CountClosedReportIDs(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG Index;
|
|
ULONG ClosedCount;
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
|
|
ClosedCount = 0;
|
|
for (Index = 0; Index < DATA_TABLE_SIZE; Index++) {
|
|
|
|
CurrEntry = DataSizeTable[Index];
|
|
|
|
while (NULL != CurrEntry) {
|
|
|
|
if (CurrEntry -> IsClosed) {
|
|
ClosedCount++;
|
|
}
|
|
|
|
CurrEntry = FORWARD_ENTRY(CurrEntry);
|
|
}
|
|
}
|
|
|
|
return (ClosedCount);
|
|
}
|
|
|
|
/*
|
|
// This function assumes that the report ID is in the given table.
|
|
*/
|
|
|
|
VOID
|
|
DataTable_CloseReportID(
|
|
IN ULONG ReportID
|
|
)
|
|
{
|
|
BOOL InTable;
|
|
ULONG Index;
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
|
|
UseBP = TRUE;
|
|
|
|
InTable = DataTable_SearchDataTable(ReportID,
|
|
&Index,
|
|
&CurrEntry
|
|
);
|
|
|
|
UseBP = FALSE;
|
|
|
|
assert (InTable);
|
|
assert (NULL != CurrEntry);
|
|
|
|
CurrEntry -> IsClosed = TRUE;
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
DataTable_GetFirstReportID(
|
|
OUT PULONG ReportID,
|
|
OUT PULONG InputSize,
|
|
OUT PULONG OutputSize,
|
|
OUT PULONG FeatureSize,
|
|
OUT PBOOL IsClosed
|
|
)
|
|
{
|
|
|
|
/*
|
|
// Set the traversal index to the first index in the table...The first
|
|
// goal is to find the first non-NULL chain in the table
|
|
*/
|
|
|
|
TraverseIndex = 0;
|
|
|
|
while (NULL == DataSizeTable[TraverseIndex] && TraverseIndex < DATA_TABLE_SIZE) {
|
|
TraverseIndex++;
|
|
}
|
|
|
|
/*
|
|
// If the index is less than the size of the data table, then we found a valid
|
|
// chain. Let's get the first entry and return the value and set traverse
|
|
// end to FALSE
|
|
*/
|
|
|
|
if (TraverseIndex < DATA_TABLE_SIZE) {
|
|
|
|
TraverseEntry = DataSizeTable[TraverseIndex];
|
|
TraverseEnd = FALSE;
|
|
|
|
*ReportID = TraverseEntry -> ReportID;
|
|
*InputSize = TraverseEntry -> InputSize;
|
|
*OutputSize = TraverseEntry -> OutputSize;
|
|
*FeatureSize = TraverseEntry -> FeatureSize;
|
|
*IsClosed = TraverseEntry -> IsClosed;
|
|
}
|
|
|
|
return (!TraverseEnd);
|
|
}
|
|
|
|
BOOL
|
|
DataTable_GetNextReportID(
|
|
PULONG ReportID,
|
|
PULONG InputSize,
|
|
PULONG OutputSize,
|
|
PULONG FeatureSize,
|
|
PBOOL IsClosed
|
|
)
|
|
{
|
|
if (!TraverseEnd) {
|
|
|
|
if (NULL == FORWARD_ENTRY(TraverseEntry)) {
|
|
|
|
do {
|
|
|
|
TraverseIndex++;
|
|
|
|
} while (NULL == DataSizeTable[TraverseIndex] && TraverseIndex < DATA_TABLE_SIZE);
|
|
|
|
if (TraverseIndex < DATA_TABLE_SIZE) {
|
|
|
|
TraverseEntry = DataSizeTable[TraverseIndex];
|
|
}
|
|
else {
|
|
TraverseEnd = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
TraverseEntry = FORWARD_ENTRY(TraverseEntry);
|
|
}
|
|
}
|
|
|
|
*ReportID = TraverseEntry -> ReportID;
|
|
*InputSize = TraverseEntry -> InputSize;
|
|
*OutputSize = TraverseEntry -> OutputSize;
|
|
*FeatureSize = TraverseEntry -> FeatureSize;
|
|
*IsClosed = TraverseEntry -> IsClosed;
|
|
|
|
return (!TraverseEnd);
|
|
}
|
|
|
|
|
|
VOID
|
|
DataTable_DestroyTable(
|
|
VOID
|
|
)
|
|
{
|
|
LONG Index;
|
|
PDATA_TABLE_ENTRY CurrEntry;
|
|
PDATA_TABLE_ENTRY NextEntry;
|
|
|
|
/*
|
|
// Ditto the comment in Init regarding this assert but good habits are
|
|
// hard to break
|
|
*/
|
|
|
|
assert (DATA_TABLE_SIZE < LONG_MAX);
|
|
|
|
for (Index = 0; Index < DATA_TABLE_SIZE; Index++) {
|
|
|
|
CurrEntry = DataSizeTable[Index];
|
|
|
|
while (NULL != CurrEntry) {
|
|
|
|
NextEntry = FORWARD_ENTRY(CurrEntry);
|
|
FREE_ENTRY(CurrEntry);
|
|
CurrEntry = NextEntry;
|
|
|
|
}
|
|
|
|
DataSizeTable[Index] = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
// Local function definitions
|
|
*/
|
|
|
|
BOOL
|
|
DataTable_SearchDataTable(
|
|
IN ULONG ReportID,
|
|
OUT PULONG Index,
|
|
OUT PDATA_TABLE_ENTRY *Entry
|
|
)
|
|
{
|
|
*Index = HASH(ReportID);
|
|
|
|
if (NULL == DataSizeTable[*Index]) {
|
|
*Entry = NULL;
|
|
return (FALSE);
|
|
}
|
|
else {
|
|
|
|
return (DataTable_SearchHashChain(DataSizeTable[*Index],
|
|
ReportID,
|
|
Entry
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DataTable_SearchHashChain(
|
|
IN PDATA_TABLE_ENTRY Chain,
|
|
IN ULONG ReportID,
|
|
OUT PDATA_TABLE_ENTRY *Entry
|
|
)
|
|
{
|
|
BOOL Found;
|
|
|
|
/*
|
|
// We'll make the assumption that chain has at least one item in it. This
|
|
// is for two reasons:
|
|
//
|
|
// 1) It makes the below search code much easier
|
|
// 2) It makes sense since why would a caller pass in a NULL pointer
|
|
// just to find out the chain is empty
|
|
*/
|
|
|
|
assert (NULL != Chain);
|
|
|
|
/*
|
|
// Initalize variables and then go...
|
|
*/
|
|
|
|
Found = FALSE;
|
|
*Entry = BACKWARD_ENTRY(Chain);
|
|
|
|
do {
|
|
|
|
/*
|
|
// Put in this assert to verify the union in the DATA_TABLE_ENTRY
|
|
// structure is correct
|
|
*/
|
|
|
|
if (ReportID == Chain -> ReportID) {
|
|
Found = TRUE;
|
|
*Entry = Chain;
|
|
break;
|
|
}
|
|
|
|
else if (ReportID > Chain -> ReportID) {
|
|
break;
|
|
}
|
|
|
|
*Entry = Chain;
|
|
Chain = FORWARD_ENTRY(Chain);
|
|
|
|
} while (NULL != Chain);
|
|
|
|
return (Found);
|
|
}
|
|
|
|
/*
|
|
// This function assumes that the given report ID value has already been
|
|
// scanned and is not in the table. If this is called and the ReportID is in
|
|
// the table, we'll probably see problems.
|
|
*/
|
|
|
|
PDATA_TABLE_ENTRY
|
|
DataTable_AddNewEntry(
|
|
IN ULONG ReportID
|
|
)
|
|
{
|
|
ULONG Index;
|
|
PDATA_TABLE_ENTRY NewEntry;
|
|
PDATA_TABLE_ENTRY PrevEntry;
|
|
BOOL InTable;
|
|
|
|
InTable = DataTable_SearchDataTable(ReportID,
|
|
&Index,
|
|
&PrevEntry
|
|
);
|
|
|
|
assert (!InTable);
|
|
|
|
/*
|
|
// If we've gotten this far, then we need to create a new entry into
|
|
// the list. PrevEntry should point to the entry before where we
|
|
// need to add the new entry. One of the following three conditions
|
|
// will exist at this point:
|
|
//
|
|
// 1) Current chain is empty -- DataSizeTable[Index] == NULL
|
|
// PrevEntry == NULL
|
|
//
|
|
// 2) NewEntry is first element in existing chain -- DataSizeTable[Index] != NULL
|
|
// -- PrevEntry == NULL;
|
|
//
|
|
// 3) NewEntry is not first element -- DataSizeTable[Index] != NULL
|
|
// -- PrevEntry != NULL
|
|
*/
|
|
|
|
NewEntry = ALLOC_ENTRY();
|
|
|
|
if (NULL != NewEntry) {
|
|
|
|
NewEntry -> ReportID = ReportID;
|
|
NewEntry -> InputSize = 0;
|
|
NewEntry -> OutputSize = 0;
|
|
NewEntry -> FeatureSize = 0;
|
|
|
|
NewEntry -> IsClosed = FALSE;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
// The links will be set in the following order
|
|
// 1) Update the links in the new entry
|
|
// 2) Change the forward link in the previous entry
|
|
// 3) Change the backward link in the following entry
|
|
*/
|
|
|
|
/*
|
|
// NewEntry's back link is simply CurrEntry no matter which of the
|
|
// above conditions
|
|
*/
|
|
|
|
BACKWARD_ENTRY(NewEntry) = PrevEntry;
|
|
|
|
/*
|
|
// NewEntry's forward link is dependent on the above conditions
|
|
*/
|
|
|
|
if (NULL != PrevEntry) {
|
|
FORWARD_ENTRY(NewEntry) = FORWARD_ENTRY(PrevEntry);
|
|
FORWARD_ENTRY(PrevEntry) = NewEntry;
|
|
}
|
|
else {
|
|
FORWARD_ENTRY(NewEntry) = DataSizeTable[Index];
|
|
DataSizeTable[Index] = NewEntry;
|
|
}
|
|
|
|
/*
|
|
// Adjust the back link in the entry following the new entry
|
|
*/
|
|
|
|
if (NULL != FORWARD_ENTRY(NewEntry)) {
|
|
BACKWARD_ENTRY(FORWARD_ENTRY(NewEntry)) = NewEntry;
|
|
}
|
|
|
|
return (NewEntry);
|
|
}
|
|
|