Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1618 lines
53 KiB

/*
* title: chid.cpp
*
* purpose: wdm kernel implementation of a hid device class
*
*/
// local prototypes
#include "hidbatt.h"
extern CHidDevice * pGlobalHidDevice[];
bool GetNextUsage(
CHidDevice * pThisDevice,
SHORT CollectionID,
USHORT NodeIndex,
USHORT usUsageIndex,
CUsage ** Usage)
{
int i;
int UsageCounter = 0;
// cycle through all the called out usages from the pHid structure
// get feature usages
for(i = 0; i < pThisDevice->m_pCaps->NumberFeatureValueCaps; i++)
{
if(pThisDevice->m_pHidDevice->FeatureValueCaps[i].LinkCollection == NodeIndex) // cardinal
{ // found a usage for this collection
if(usUsageIndex == UsageCounter)
{
// got a usage to send back
CUsage * pThisUsage = new (NonPagedPool, HidBattTag) CUsage();
if (!pThisUsage) {
// Could not allocate new CUsage, return error
return FALSE;
}
pThisUsage->m_pValueCaps = &pThisDevice->m_pHidDevice->FeatureValueCaps[i];
pThisUsage->m_eType = eFeatureValue;
*Usage = pThisUsage;
return TRUE;
}
UsageCounter++;
}
}
// ditto feature buttons
for(i = 0; i < pThisDevice->m_pCaps->NumberFeatureButtonCaps; i++)
{
if(pThisDevice->m_pHidDevice->FeatureButtonCaps[i].LinkCollection == NodeIndex )
{ // found a usage for this collection
if(usUsageIndex == UsageCounter)
{
// got a usage to send back
CUsage * pThisUsage = new (NonPagedPool, HidBattTag) CUsage();
if (!pThisUsage) {
// Could not allocate new CUsage, return error
return FALSE;
}
pThisUsage->m_pButtonCaps = &pThisDevice->m_pHidDevice->FeatureButtonCaps[i];
pThisUsage->m_eType = eFeatureButton;
*Usage = pThisUsage;
return TRUE;
}
UsageCounter++;
}
}
// ditto input values
for(i = 0; i < pThisDevice->m_pCaps->NumberInputValueCaps; i++)
{
if(pThisDevice->m_pHidDevice->InputValueCaps[i].LinkCollection == NodeIndex)
{ // found a usage for this collection
if(usUsageIndex == UsageCounter)
{
// got a usage to send back
CUsage * pThisUsage = new (NonPagedPool, HidBattTag) CUsage();
if (!pThisUsage) {
// Could not allocate new CUsage, return error
return FALSE;
}
pThisUsage->m_pValueCaps = &pThisDevice->m_pHidDevice->InputValueCaps[i];
pThisUsage->m_eType = eInputValue;
*Usage = pThisUsage;
return TRUE;
}
UsageCounter++;
}
}
// ditto input buttons
for(i = 0; i < pThisDevice->m_pCaps->NumberInputButtonCaps; i++)
{
if(pThisDevice->m_pHidDevice->InputButtonCaps[i].LinkCollection == NodeIndex)
{ // found a usage for this collection
if(usUsageIndex == UsageCounter)
{
// got a usage to send back
CUsage * pThisUsage = new (NonPagedPool, HidBattTag) CUsage();
if (!pThisUsage) {
// Could not allocate new CUsage, return error
return FALSE;
}
pThisUsage->m_pButtonCaps = &pThisDevice->m_pHidDevice->InputButtonCaps[i];
pThisUsage->m_eType = eInputButton;
*Usage = pThisUsage;
return TRUE;
}
UsageCounter++;
}
}
// ditto Output values
for(i = 0; i < pThisDevice->m_pCaps->NumberOutputValueCaps; i++)
{
if(pThisDevice->m_pHidDevice->OutputValueCaps[i].LinkCollection == NodeIndex)
{ // found a usage for this collection
if(usUsageIndex == UsageCounter)
{
// got a usage to send back
CUsage * pThisUsage = new (NonPagedPool, HidBattTag) CUsage();
if (!pThisUsage) {
// Could not allocate new CUsage, return error
return FALSE;
}
pThisUsage->m_pValueCaps = &pThisDevice->m_pHidDevice->OutputValueCaps[i];
pThisUsage->m_eType = eOutputValue;
*Usage = pThisUsage;
return TRUE;
}
UsageCounter++;
}
}
// ditto Output buttons
for(i = 0; i < pThisDevice->m_pCaps->NumberOutputButtonCaps; i++)
{
if(pThisDevice->m_pHidDevice->OutputButtonCaps[i].LinkCollection == NodeIndex)
{ // found a usage for this collection
if(usUsageIndex == UsageCounter)
{
// got a usage to send back
CUsage * pThisUsage = new (NonPagedPool, HidBattTag) CUsage();
if (!pThisUsage) {
// Could not allocate new CUsage, return error
return FALSE;
}
pThisUsage->m_pButtonCaps = &pThisDevice->m_pHidDevice->OutputButtonCaps[i];
pThisUsage->m_eType = eOutputButton;
*Usage = pThisUsage;
return TRUE;
}
UsageCounter++;
}
}
return FALSE;
}
CTypeMask::CTypeMask()
{
// set members to zero
ReportType = 0;
bWriteable = 0;
bIsString = 0;
bIsNumber = 0;
bAlertable = 0;
bVolatile = 0;
}
CProperties::CProperties(CUsage * pUsage)
{
PHIDP_BUTTON_CAPS pButtCaps; // fill in the properties for this usage
PHIDP_VALUE_CAPS pValueCaps;
eHidType eType = pUsage->m_eType; // Cash local copy so compiler (and PREFAST
// will know this value doesn't change.
// (This causes more compact compiling as well)
switch(eType)
{
case eFeatureButton:
case eInputButton:
case eOutputButton:
pButtCaps = pUsage->m_pButtonCaps;
m_UnitExponent = 0; // no exponents on buttons
m_Unit = 0; // buttons don't have units
m_LogicalMin = 0; // ditto for max and min pCaps->LogicalMin;
m_LogicalMax = 0; // pCaps->LogicalMax;
m_LinkCollection = pButtCaps->LinkCollection;
m_ReportID = pButtCaps->ReportID;
m_Usage = pButtCaps->NotRange.Usage;
m_UsagePage = pButtCaps->UsagePage;
break;
case eFeatureValue:
case eInputValue:
case eOutputValue:
pValueCaps = pUsage->m_pValueCaps;
m_Unit = pValueCaps->Units;
m_UnitExponent = (SHORT) pValueCaps->UnitsExp;
m_LogicalMin = pValueCaps->LogicalMin;
m_LogicalMax = pValueCaps->LogicalMax;
m_LinkCollection = pValueCaps->LinkCollection;
m_ReportID = pValueCaps->ReportID;
m_Usage = pValueCaps->NotRange.Usage;
m_UsagePage = pValueCaps->UsagePage;
break;
}
// setup type mask
m_pType = new (NonPagedPool, HidBattTag) CTypeMask();
if (m_pType) {
if(eType == eInputButton || eType == eInputValue)
{
m_pType->SetAlertable();
}
// set writability
if(eType == eFeatureButton || eType == eOutputButton)
{
if(pButtCaps->BitField & 0x01)
{
m_pType->SetIsWriteable();
}
} else if(eType == eFeatureValue || eType == eOutputValue)
{
if(pValueCaps->BitField & 0x01)
{
m_pType->SetIsWriteable();
}
}
// set volatility
if(eType == eFeatureValue)
{
if(pValueCaps->BitField & 0x80)
{
m_pType->SetVolatile();
}
}
if(eType == eFeatureButton)
{
if(pButtCaps->BitField & 0x80)
{
m_pType->SetVolatile();
}
}
switch(eType)
{
case eFeatureButton:
case eFeatureValue:
m_pType->SetReportType(FeatureType);
break;
case eInputButton:
case eInputValue:
m_pType->SetReportType(InputType);
break;
case eOutputButton:
case eOutputValue:
m_pType->SetReportType(OutputType);
}
// set value to to number until I figure out how to do strings
m_pType->SetIsNumber();
}
}
CProperties::~CProperties()
{
if (m_pType) {
delete m_pType;
m_pType = NULL;
}
return;
}
CUsagePath::CUsagePath(USAGE UsagePage, USAGE UsageID, CUsage * pThisUsage)
{
// init members
m_UsagePage = UsagePage;
m_UsageNumber = UsageID;
m_pUsage = pThisUsage;
m_pNextEntry = NULL;
return;
}
CHidDevice::CHidDevice()
{
// clear out usage arrays
for(int i = 0; i<MAXREPORTID; i++)
{
m_InputUsageArrays[i] = NULL;
m_FeatureBuffer[i] = NULL;
m_ReportIdArray[i] = 0;
}
m_pThreadObject = NULL;
m_pReadBuffer = NULL;
m_pEventHandler = 0;
}
bool CHidDevice::OpenHidDevice(PDEVICE_OBJECT pDeviceObject)
{
NTSTATUS ntStatus;
ULONG ulNodeCount;
bool bResult;
HID_COLLECTION_INFORMATION collectionInformation;
HIDDebugBreak(HIDBATT_BREAK_ALWAYS);
HidBattPrint(HIDBATT_TRACE,("CHidDevice::OpenHidDevice\n"));
// first get collection information for this device
ntStatus = DoIoctl(
pDeviceObject,
IOCTL_HID_GET_COLLECTION_INFORMATION,
NULL,
0,
&collectionInformation,
sizeof(HID_COLLECTION_INFORMATION),
(CHidDevice *) NULL
);
if(NT_ERROR(ntStatus))
{
return FALSE;
}
m_pPreparsedData = (PHIDP_PREPARSED_DATA)
ExAllocatePoolWithTag(NonPagedPool,
collectionInformation.DescriptorSize,
HidBattTag);
if(!m_pPreparsedData)
{
return FALSE;
}
ntStatus = DoIoctl(
pDeviceObject,
IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
NULL,
0,
m_pPreparsedData,
collectionInformation.DescriptorSize,
(CHidDevice *) NULL
);
if(NT_ERROR(ntStatus))
{
ExFreePool(m_pPreparsedData);
return FALSE;
}
// init the caps structure
m_pCaps = (PHIDP_CAPS) ExAllocatePoolWithTag (NonPagedPool,
sizeof(HIDP_CAPS),
HidBattTag);
if(!m_pCaps)
{
ExFreePool(m_pPreparsedData);
return FALSE;
}
RtlZeroMemory(m_pCaps,sizeof(HIDP_CAPS));
ntStatus = HidP_GetCaps (m_pPreparsedData, m_pCaps);
if (NT_ERROR(ntStatus))
{
ExFreePool(m_pPreparsedData);
ExFreePool(m_pCaps);
return FALSE;
}
// set usage page and usage for application level
m_UsagePage = m_pCaps->UsagePage;
m_UsageID = m_pCaps->Usage;
// init the collection array
ulNodeCount = m_pCaps->NumberLinkCollectionNodes;
HIDP_LINK_COLLECTION_NODE * pLinkNodes = (HIDP_LINK_COLLECTION_NODE*)
ExAllocatePoolWithTag (NonPagedPool,
sizeof(HIDP_LINK_COLLECTION_NODE) * ulNodeCount,
HidBattTag);
if(!pLinkNodes) return FALSE;
RtlZeroMemory(pLinkNodes,sizeof(HIDP_LINK_COLLECTION_NODE) * ulNodeCount );
ntStatus = HidP_GetLinkCollectionNodes(
pLinkNodes,
&ulNodeCount,
m_pPreparsedData
);
if(ntStatus != HIDP_STATUS_SUCCESS)
{
ExFreePool( m_pPreparsedData);
ExFreePool(m_pCaps);
ExFreePool(pLinkNodes);
return FALSE;
}
// the following call will init all the collections in the device
CCollectionArray * ThisArray = new (NonPagedPool, HidBattTag) CCollectionArray(pLinkNodes,(USHORT) ulNodeCount, -1);
if (!ThisArray) {
// Creation of the collection failed, return failure
return FALSE;
}
m_CollectionArray = ThisArray;
// have each collection fill in its usage array
// this call uses KR's methods to access and setup his original hid structures.
// ... This data is then used to populate the the hid device class structures
m_pHidDevice = SetupHidData(
m_pPreparsedData,
m_pCaps,
pLinkNodes);
for(int i = 0; i < ThisArray->m_CollectionCount; i++)
{
ThisArray->m_pCollections[i]->InitUsages(this);
}
ExFreePool(pLinkNodes);
return TRUE;
}
CHidDevice::~CHidDevice()
{
ULONG i;
// release any allocated memory and cleanup
if (m_pHidDevice) {
if (m_pHidDevice->InputButtonCaps) {
ExFreePool (m_pHidDevice->InputButtonCaps);
}
if (m_pHidDevice->InputValueCaps) {
ExFreePool (m_pHidDevice->InputValueCaps);
}
if (m_pHidDevice->OutputButtonCaps) {
ExFreePool (m_pHidDevice->OutputButtonCaps);
}
if (m_pHidDevice->OutputValueCaps) {
ExFreePool (m_pHidDevice->OutputValueCaps);
}
if (m_pHidDevice->FeatureButtonCaps) {
ExFreePool (m_pHidDevice->FeatureButtonCaps);
}
if (m_pHidDevice->FeatureValueCaps) {
ExFreePool (m_pHidDevice->FeatureValueCaps);
}
ExFreePool (m_pHidDevice);
}
if(m_CollectionArray) {
delete m_CollectionArray;
m_CollectionArray = NULL;
}
for (i = 0; i < MAXREPORTID; i++) {
if(m_InputUsageArrays[i]) {
if (m_InputUsageArrays[i]->m_pUsages) {
ExFreePool (m_InputUsageArrays[i]->m_pUsages);
}
ExFreePool (m_InputUsageArrays[i]);
m_InputUsageArrays[i] = NULL;
}
}
if(m_pReadBuffer) {
ExFreePool (m_pReadBuffer);
m_pReadBuffer = NULL;
}
for (i = 0; i < MAXREPORTID; i++) {
if(m_FeatureBuffer[i]) {
ExFreePool (m_FeatureBuffer[i]);
m_FeatureBuffer[i] = NULL;
}
}
if (m_pPreparsedData) {
ExFreePool (m_pPreparsedData);
}
if (m_pCaps) {
ExFreePool (m_pCaps);
}
return;
}
CUsage * CHidDevice::FindUsage(CUsagePath * PathToUsage, USHORT usType)
{
int i = 0;
CCollection * pActiveCollection = (CCollection *) NULL;
CCollectionArray * pCurrentCArray = m_CollectionArray;
// Index into collection array by usage page : usage id
while(PathToUsage->m_pNextEntry)
{
// traversing a collection
while( pCurrentCArray && i < pCurrentCArray->m_CollectionCount)
{
if(pCurrentCArray->m_pCollections[i]->m_UsagePage == PathToUsage->m_UsagePage &&
pCurrentCArray->m_pCollections[i]->m_CollectionID == PathToUsage->m_UsageNumber)
{
// found a node, go down a level
pActiveCollection = pCurrentCArray->m_pCollections[i];
pCurrentCArray = pCurrentCArray->m_pCollections[i]->m_CollectionArray;
i = 0;
break;
}
i++;
}
if(i) return (CUsage *) NULL; // not found
PathToUsage = PathToUsage->m_pNextEntry;
}
if(!pActiveCollection) return (CUsage *) NULL; // no collection found, shouldn't get here
// got to the collection, check its usages
CUsageArray * pCurrentUArray = pActiveCollection->m_UsageArray;
if(!pCurrentUArray) return (CUsage *) NULL;
// interate usage array
for(i = 0; i < pCurrentUArray->m_UsageCount; i++)
{
if(pCurrentUArray->m_pUsages[i]->m_pProperties->m_Usage == PathToUsage->m_UsageNumber &&
pCurrentUArray->m_pUsages[i]->m_pProperties->m_UsagePage == PathToUsage->m_UsagePage)
{
// got it !
if(usType == WRITEABLE) // writable returns feature and output usages
if(pCurrentUArray->m_pUsages[i]->m_eType == eFeatureValue ||
pCurrentUArray->m_pUsages[i]->m_eType == eOutputValue ||
pCurrentUArray->m_pUsages[i]->m_eType == eFeatureButton ||
pCurrentUArray->m_pUsages[i]->m_eType == eOutputButton)
// return writeable usage !
return pCurrentUArray->m_pUsages[i];
if(usType == READABLE) // returns input and feature types
if(pCurrentUArray->m_pUsages[i]->m_eType == eFeatureValue ||
pCurrentUArray->m_pUsages[i]->m_eType == eInputValue ||
pCurrentUArray->m_pUsages[i]->m_eType == eFeatureButton ||
pCurrentUArray->m_pUsages[i]->m_eType == eInputButton)
// return readable usage !
return pCurrentUArray->m_pUsages[i];
}
}
return (CUsage *) NULL;
}
NTSTATUS CHidDevice::ActivateInput()
{
NTSTATUS ntStatus;
HANDLE hReadThread;
// init notification elements
HidBattPrint(HIDBATT_TRACE, ("ActivateInput entered\n"));
if(!m_pReadBuffer)
{
if (!m_pCaps->InputReportByteLength) {
HidBattPrint(HIDBATT_ERROR, ("ActivateInput: InputReportByteLength = %08x; NumberInputButtonCaps = %08x; NumberInputValueCaps = %08x\n",
m_pCaps->InputReportByteLength,
m_pCaps->NumberInputButtonCaps,
m_pCaps->NumberInputValueCaps));
//
// This just means that the battery doesn't give notifications.
//
return STATUS_SUCCESS;
}
m_pReadBuffer = (PBYTE) ExAllocatePoolWithTag (NonPagedPool,
m_pCaps->InputReportByteLength,
HidBattTag);
if(!m_pReadBuffer)
return STATUS_INSUFFICIENT_RESOURCES;
}
// init read event
KeInitializeEvent(&m_kReadEvent,NotificationEvent,FALSE);
ntStatus = PsCreateSystemThread(
OUT &hReadThread,
IN THREAD_ALL_ACCESS,
IN NULL, // POBJECT_ATTRIBUTES ObjectAttributes
IN NULL, // HANDLE ProcessHandle
OUT NULL, // PCLIENT_ID ClientId
IN ReadThread,
IN this
);
if(NT_ERROR(ntStatus))
{
// kill refresh loop, then break
HidBattPrint(HIDBATT_TRACE, ("ActivateInput error, exiting - Status = %x\n",ntStatus));
ExFreePool(m_pReadBuffer);
m_pReadBuffer = NULL;
return ntStatus;
}
HidBattPrint(HIDBATT_TRACE, ("ActivateInput exiting = Status = %x\n",ntStatus));
ntStatus = ObReferenceObjectByHandle (
hReadThread,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
&m_pThreadObject,
NULL
);
if (!NT_SUCCESS (ntStatus)) {
HidBattPrint(HIDBATT_ERROR, ("ActivateInput can't get thread object\n",ntStatus));
// Return without closing hReadThread handle.
// This will cause a reboot to be needed to fully unload the driver.
return STATUS_SUCCESS;
}
ntStatus = ZwClose (hReadThread);
// Ignore return value. We can't do anything about a failure, and the
// driver will still work. It will simply result in leaking a thread
// object when the device is removed.
return STATUS_SUCCESS;
}
NTSTATUS ReadCompletionRoutine(PDEVICE_OBJECT pDO, PIRP pIrp,PVOID pContext)
{
CHidDevice * pHidDevice = (CHidDevice *) pContext;
HidBattPrint(HIDBATT_TRACE,("Read Completed, IO Status = %x\n",pIrp->IoStatus.Status));
KeSetEvent(&pHidDevice->m_kReadEvent,0,FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
void _stdcall ReadThread(PVOID pContext)
{
USHORT usFailureCount = 0;
// build a read irp for hid class
USHORT usEventIndex = 0;
NTSTATUS ntStatus = STATUS_SUCCESS;
PVOID EventArray[2];
PIO_STACK_LOCATION pNewStack;
CBatteryDevExt * pDevExt;
PMDL mdl;
HIDDebugBreak(HIDBATT_BREAK_ALWAYS);
HidBattPrint(HIDBATT_TRACE,("Read Thread entered\n"));
//
// first get our "this"
//
CHidDevice * pHidDev = (CHidDevice *) pContext;
pDevExt = (CBatteryDevExt *) pHidDev->m_pEventContext;
//
// Hold the remove lock so the remove routine doesn't cancel the irp while
// we are playing with it.
//
if (!NT_SUCCESS (IoAcquireRemoveLock (&pDevExt->m_StopLock, (PVOID) HidBattTag))) {
goto ReadThreadCleanup1; // fail
}
//
// Allocate Irp to be used and re-used
//
pHidDev->m_pReadIrp = IoAllocateIrp (pHidDev->m_pLowerDeviceObject->StackSize, FALSE);
if(!pHidDev->m_pReadIrp) {
goto ReadThreadCleanup1; // fail
}
//
// Create MDL
//
mdl = IoAllocateMdl( pHidDev->m_pReadBuffer,
pHidDev->m_pCaps->InputReportByteLength,
FALSE,
FALSE,
(PIRP) NULL );
if (!mdl) {
goto ReadThreadCleanup2;
}
//
// Lock IO buffer
//
__try {
MmProbeAndLockPages( mdl,
KernelMode,
(LOCK_OPERATION) IoWriteAccess );
} __except(EXCEPTION_EXECUTE_HANDLER) {
ntStatus = STATUS_UNSUCCESSFUL;
}
if (!NT_SUCCESS(ntStatus)) {
goto ReadThreadCleanup3;
}
while (TRUE) {
IoReuseIrp (pHidDev->m_pReadIrp, STATUS_SUCCESS);
pHidDev->m_pReadIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pHidDev->m_pReadIrp->MdlAddress = mdl;
IoSetCompletionRoutine(pHidDev->m_pReadIrp,ReadCompletionRoutine,pHidDev,TRUE,TRUE,TRUE);
pNewStack= IoGetNextIrpStackLocation(pHidDev->m_pReadIrp);
pNewStack->FileObject = pHidDev->m_pFCB;
pNewStack->MajorFunction = IRP_MJ_READ;
pNewStack->Parameters.Read.Length = pHidDev->m_pCaps->InputReportByteLength;
pNewStack->Parameters.Read.ByteOffset.QuadPart = 0;
KeResetEvent(&pHidDev->m_kReadEvent);
ntStatus = IoCallDriver(pHidDev->m_pLowerDeviceObject,pHidDev->m_pReadIrp);
//
// Don't hold the lock while we are waiting for the IRP to complete.
// The remove routine will cancel the irp if needed.
//
IoReleaseRemoveLock (&pDevExt->m_StopLock, (PVOID) HidBattTag);
if (ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(
&pHidDev->m_kReadEvent,
Executive,
KernelMode,
FALSE,
NULL
);
ntStatus = pHidDev->m_pReadIrp->IoStatus.Status;
}
// we awoke on a read completion
HidBattPrint(HIDBATT_TRACE,("Read woke: status = 0x%08x\n", ntStatus));
if(ntStatus != STATUS_SUCCESS)
{
if(ntStatus == STATUS_DEVICE_NOT_CONNECTED
|| ntStatus == STATUS_CANCELLED)
{
HidBattPrint(HIDBATT_ERROR,("Read Failure - Status = %x\n",ntStatus));
break;
}
usFailureCount++;
if(usFailureCount++ == 10)
{
// stop trying
HidBattPrint(HIDBATT_ERROR,("Read Failure - More than 10 retries\nStatus = %x\n",pHidDev->m_pReadIrp->IoStatus.Status));
break;
}
//
// Hold the lock while playing with the IRP
// If we are being removed, we need to break out.
//
if (!NT_SUCCESS (IoAcquireRemoveLock (&pDevExt->m_StopLock, (PVOID) HidBattTag))) {
break;
}
continue;
}
usFailureCount = 0;
//
// Hold the lock while playing with the IRP
// If we are being removed, we need to break out.
//
if (!NT_SUCCESS (IoAcquireRemoveLock (&pDevExt->m_StopLock, (PVOID) HidBattTag))) {
break;
}
// process input buffer
USHORT usReportId = pHidDev->m_pReadBuffer[0];
USHORT usIndex = pHidDev->GetIndexFromReportId(usReportId);
if(usIndex == MAXREPORTID) // is this a report we recognize
{
HidBattPrint(HIDBATT_TRACE,("Read: don't recognize report: usIndex = 0x%08x\n", usIndex));
continue; // we don't recognize this report
}
CUsageArray *pThisInputArray = pHidDev->m_InputUsageArrays[usIndex];
if(!pThisInputArray)
{
HidBattPrint(HIDBATT_TRACE,("Read: nothing to update\n"));
continue; // nothing to update
}
for(int i=0; i< pThisInputArray->m_UsageCount; i++)
{
HidBattPrint(HIDBATT_TRACE,("Read: Getting value\n"));
pThisInputArray->m_pUsages[i]->GetValue();
}
} //while
//
// Cleanup
//
MmUnlockPages( mdl );
ReadThreadCleanup3:
IoFreeMdl(mdl);
ReadThreadCleanup2:
// Irp will be freed by Query remove/stop device thread after read thread has terminated.
ReadThreadCleanup1:
pDevExt->m_pBattery->m_Tag = BATTERY_TAG_INVALID;
PsTerminateSystemThread(STATUS_SUCCESS);
HidBattPrint(HIDBATT_ERROR,("Read thread terminated: Why am I seeing this?\n"));
}
// collectionarray methods
CCollectionArray::CCollectionArray(PHIDP_LINK_COLLECTION_NODE pTheNodes, USHORT usNodeCount, SHORT sParentIndex)
{
HIDDebugBreak(HIDBATT_BREAK_NEVER);
USHORT i;
m_pCollections = 0;
m_CollectionCount = 0;
if(sParentIndex == -1) // exception processing for application level collection
{
m_pCollections = (CCollection **)
ExAllocatePoolWithTag (NonPagedPool,
sizeof(CCollection *),
HidBattTag);
if(!m_pCollections) return;
RtlZeroMemory(m_pCollections,sizeof(CCollection *));
m_pCollections[0] = new (NonPagedPool, HidBattTag) CCollection(pTheNodes,usNodeCount, 0); // get my children
m_CollectionCount = 1;
} else {
for( i = 1; i < usNodeCount; i++) {
PHIDP_LINK_COLLECTION_NODE pThisNode = &pTheNodes[i];
if( (pTheNodes[i].Parent == sParentIndex) ) {
m_CollectionCount++; // inc collection count
if(!m_pCollections) {
m_pCollections = (CCollection **)
ExAllocatePoolWithTag (NonPagedPool,
sizeof(CCollection *),
HidBattTag);
if(!m_pCollections) return;
RtlZeroMemory(m_pCollections,sizeof(CCollection *));
} else {
// make the array bigger
//m_Collections = (CCollection **) realloc(m_Collections,(m_CollectionCount * sizeof(CCollection *)));
CCollection ** pTemp = m_pCollections;
m_pCollections = (CCollection **)
ExAllocatePoolWithTag (NonPagedPool,
m_CollectionCount * sizeof(CCollection *),
HidBattTag);
if (!m_pCollections) {
// Re-allocation failure, print error and revert to previous state and return
HidBattPrint(HIDBATT_ERROR, ("CCollectionArray: Could not resize CCollection"));
m_pCollections = pTemp;
m_CollectionCount--;
return;
}
RtlCopyMemory(m_pCollections,pTemp,(m_CollectionCount -1) * sizeof(CCollection *));
ExFreePool(pTemp);
}
// add collection to array
CCollection * TempCollection = new (NonPagedPool, HidBattTag) CCollection(pTheNodes,usNodeCount,i);
if (!TempCollection) {
// Could not allocate new CCollection, print debug message and return
HidBattPrint(HIDBATT_ERROR, ("CCollectionArray: Could not allocate new CCollection"));
return;
}
m_pCollections[m_CollectionCount-1] = TempCollection;
}
}
}
}
CCollectionArray::~CCollectionArray()
{
while(m_CollectionCount) {
delete m_pCollections[--m_CollectionCount];
}
if (m_pCollections) {
ExFreePool (m_pCollections);
}
return;
}
CCollection::CCollection(PHIDP_LINK_COLLECTION_NODE pTheNodes, USHORT usNodeCount,USHORT usNodeIndex)
{
m_UsageArray = (CUsageArray *) NULL;
m_CollectionArray = (CCollectionArray *) NULL; // init vars
// setup this collection
CCollectionArray * ThisArray = new (NonPagedPool, HidBattTag) CCollectionArray(pTheNodes,usNodeCount,usNodeIndex);
if (ThisArray) {
if(!ThisArray->m_CollectionCount) {// any child collections
delete ThisArray;
} else {
m_CollectionArray = ThisArray;
}
}
// set the info in this collection
m_CollectionID = pTheNodes[usNodeIndex].LinkUsage;
m_UsagePage = pTheNodes[usNodeIndex].LinkUsagePage;
m_NodeIndex = usNodeIndex;
}
CCollection::~CCollection()
{
// delete all usages in the usage array
if(m_UsageArray)
{
delete m_UsageArray;
}
// delete all the sub collections
if(m_CollectionArray)
{
delete m_CollectionArray;
}
}
void CCollection::InitUsages(CHidDevice * ThisDevice)
{
bool bResult = FALSE;
USHORT usUsageIndex = 0;
CUsage * pThisUsage ;
USHORT usInputIndex;
HIDDebugBreak(HIDBATT_BREAK_NEVER);
while( bResult = GetNextUsage(ThisDevice,m_CollectionID,m_NodeIndex,usUsageIndex,&pThisUsage))
{
if(!m_UsageArray) // if first usage
{
m_UsageArray = new (NonPagedPool, HidBattTag) CUsageArray();
if (!m_UsageArray) {
delete pThisUsage;
return;
}
}
pThisUsage->SetCapabilities();
pThisUsage->m_pHidDevice = ThisDevice; // store this usage's device pointer
if(pThisUsage->m_eType == eInputButton ||
pThisUsage->m_eType == eInputValue)
{
// get input array index
usInputIndex = ThisDevice->AssignIndexToReportId(pThisUsage->m_pProperties->m_ReportID);
if(!ThisDevice->m_InputUsageArrays[usInputIndex])
{
// no array in this report position yet, create
ThisDevice->m_InputUsageArrays[usInputIndex] = new (NonPagedPool, HidBattTag) CUsageArray();
if (!(ThisDevice->m_InputUsageArrays[usInputIndex])) return;
}
ThisDevice->m_InputUsageArrays[usInputIndex]->AddUsage(pThisUsage); // add input usages to refresh stack
}
usUsageIndex++;
m_UsageArray->AddUsage(pThisUsage);
}
// also init all my collections
if(!m_CollectionArray) return;
for(int i = 0; i < m_CollectionArray->m_CollectionCount; i++)
{
m_CollectionArray->m_pCollections[i]->InitUsages(ThisDevice);
}
}
USHORT CHidDevice::AssignIndexToReportId(USHORT usReportId)
{
USHORT i;
HidBattPrint(HIDBATT_TRACE,("AssignIndexToReportId: ReportId = %x -- ", usReportId));
for(i = 0;i < MAXREPORTID; i++)
{
if(!m_ReportIdArray[i]) {
HidBattPrint(HIDBATT_TRACE,("Assigning to %x\n", i));
m_ReportIdArray[i] = usReportId;
return i;
}
if(m_ReportIdArray[i] == usReportId) {
HidBattPrint(HIDBATT_TRACE,("Already assigned to %x\n", i));
return i;
}
}
//
// It would be really nice if we could dynamically allocate more
// since there isn't a small limit set to the number of report IDs.
//
ASSERTMSG("MAXREPORTID exceeded.\n", FALSE);
return 0;
}
USHORT CHidDevice::GetIndexFromReportId(USHORT usReportId)
{
USHORT i;
HidBattPrint(HIDBATT_TRACE,("GetIndexFromReportId: ReportId = %x\n", usReportId));
for(i = 0; i< MAXREPORTID; i++) {
if(m_ReportIdArray[i] == usReportId) {
return i;
}
}
HidBattPrint(HIDBATT_TRACE,("GetIndexFromReportId: Failed\n", usReportId));
return i; // error return is MAXREPORTIDS
}
CUsagePath * CCollection::FindIndexedUsage(USHORT * pCurrentIndex, USHORT TargetIndex)
{
CUsagePath * ThisPath;
CUsagePath * NewPath;
if(m_UsageArray)
{
if(m_UsageArray->m_UsageCount + *pCurrentIndex > TargetIndex)
{
// do the arithmetic to get the current index
int ThisIndex = TargetIndex - *pCurrentIndex;
// found it, construct usage path
ThisPath = new (NonPagedPool, HidBattTag) CUsagePath(
m_UsagePage,
m_CollectionID,
m_UsageArray->m_pUsages[ThisIndex]
);
return ThisPath;
}
// didn't find it, inc current index
*pCurrentIndex += m_UsageArray->m_UsageCount;
}
if(!m_CollectionArray) return NULL; // nothing more, quit
// call out the sub collections
for(int i = 0; i < m_CollectionArray->m_CollectionCount; i++)
{
ThisPath = m_CollectionArray->m_pCollections[i]->FindIndexedUsage(pCurrentIndex, TargetIndex);
if(ThisPath)
{
// one of our subcollections had the usage, add us to the path
NewPath = new (NonPagedPool, HidBattTag) CUsagePath(
m_UsagePage,
m_CollectionID,
NULL
);
if (!NewPath) {
delete ThisPath;
return NULL;
}
NewPath->m_pNextEntry = ThisPath;
return NewPath;
}
}
return NULL;
}
CUsagePath::~CUsagePath()
{
CUsagePath * tempNextEntry;
tempNextEntry = m_pNextEntry;
m_pNextEntry = NULL;
if (tempNextEntry) {
delete tempNextEntry;
}
return;
}
CUsage::CUsage()
{
// do member init
m_pProperties = (CProperties *) NULL;
m_pButtonCaps = (HIDP_BUTTON_CAPS *) NULL;
m_pValueCaps = (HIDP_VALUE_CAPS *)NULL;
m_eType = (eHidType) 0;
m_Value = 0;
m_String = (char *)NULL;
return;
}
CUsage::~CUsage()
{
// free all associated objects
if(m_pProperties) {
delete m_pProperties;
}
return;
}
bool CUsage::GetValue()
{
NTSTATUS ntStatus;
ULONG ulResult;
bool bResult;
BYTE ReportID;
USHORT usDummy;
HIDDebugBreak(HIDBATT_BREAK_NEVER);
switch(m_eType)
{
case eFeatureValue:
case eFeatureButton:
ReportID = (BYTE) m_pProperties->m_ReportID;
// do we have the report that contains this data?
if(!m_pHidDevice->m_FeatureBuffer[ReportID])
{
// must first create and fill buffer from getfeature
// allocate memory
m_pHidDevice->m_FeatureBuffer[ReportID] =
(PBYTE) ExAllocatePoolWithTag (NonPagedPool,
m_pHidDevice->m_pCaps->FeatureReportByteLength+1,
HidBattTag);
RtlZeroMemory(m_pHidDevice->m_FeatureBuffer[ReportID],
m_pHidDevice->m_pCaps->FeatureReportByteLength+1);
// setup first byte of buffer to report id
*m_pHidDevice->m_FeatureBuffer[ReportID] = ReportID;
// now read in report
HIDDebugBreak(HIDBATT_BREAK_DEBUG);
HidBattPrint(HIDBATT_TRACE,("GetFeature\n"));
ntStatus = DoIoctl(
m_pHidDevice->m_pLowerDeviceObject,
IOCTL_HID_GET_FEATURE,
NULL,
0,
m_pHidDevice->m_FeatureBuffer[ReportID],
m_pHidDevice->m_pCaps->FeatureReportByteLength+1,
m_pHidDevice
);
if (!NT_SUCCESS (ntStatus)) {
HidBattPrint(HIDBATT_DATA,("GetFeature - IOCTL_HID_GET_FEATURE 0x%08x\n", ntStatus));
}
}
if(m_pProperties->m_pType->IsVolatile())
{
// this is a volatile value, refresh report
RtlZeroMemory(m_pHidDevice->m_FeatureBuffer[ReportID],
m_pHidDevice->m_pCaps->FeatureReportByteLength+1);
// setup first byte of buffer to report id
*m_pHidDevice->m_FeatureBuffer[ReportID] = ReportID;
// now read in report
HIDDebugBreak(HIDBATT_BREAK_NEVER);
HidBattPrint(HIDBATT_TRACE,("GetFeature - Refresh\n"));
ntStatus = DoIoctl(
m_pHidDevice->m_pLowerDeviceObject,
IOCTL_HID_GET_FEATURE,
NULL,
0,
m_pHidDevice->m_FeatureBuffer[ReportID],
m_pHidDevice->m_pCaps->FeatureReportByteLength+1,
m_pHidDevice
);
if (!NT_SUCCESS (ntStatus)) {
HidBattPrint(HIDBATT_DATA,("GetFeature - (volitile) IOCTL_HID_GET_FEATURE 0x%08x\n", ntStatus));
// return error, fixes bug #357483
return FALSE;
}
}
if(m_eType == eFeatureValue)
{
ntStatus = HidP_GetUsageValue(
HidP_Feature,
m_pProperties->m_UsagePage,
m_pProperties->m_LinkCollection,
m_pProperties->m_Usage,
&ulResult,
m_pHidDevice->m_pPreparsedData,
(char *) m_pHidDevice->m_FeatureBuffer[ReportID],
m_pHidDevice->m_pCaps->FeatureReportByteLength
);
if(!NT_SUCCESS(ntStatus))
{
HidBattPrint(HIDBATT_DATA,("GetFeature - HidP_GetUsageValue 0x%08x\n", ntStatus));
// return error
return FALSE;
}
} else
{
// must get button data
ULONG Buttons, BufferSize;
PUSAGE pButtonBuffer;
Buttons = HidP_MaxUsageListLength (HidP_Feature, 0, m_pHidDevice->m_pPreparsedData);
BufferSize = Buttons * sizeof(USAGE);
pButtonBuffer = (PUSAGE) ExAllocatePoolWithTag (NonPagedPool,
BufferSize,
HidBattTag);
if (pButtonBuffer==NULL) {
HidBattPrint(HIDBATT_DATA,("GetFeature - ExAllocatePoolWithTag returned NULL."));
// return error
return FALSE;
}
else {
RtlZeroMemory(pButtonBuffer,BufferSize);
ntStatus = HidP_GetButtons(
HidP_Feature,
m_pProperties->m_UsagePage,
m_pProperties->m_LinkCollection,
(PUSAGE) pButtonBuffer,
&Buttons,
m_pHidDevice->m_pPreparsedData,
(char *) m_pHidDevice->m_FeatureBuffer[ReportID],
m_pHidDevice->m_pCaps->FeatureReportByteLength
);
}
if(!NT_SUCCESS(ntStatus))
{
HidBattPrint(HIDBATT_DATA,("GetFeature - HidP_GetButtons 0x%08x\n", ntStatus));
// return error
return FALSE;
}
// get the value for the requested button
PUSAGE pUsage = (PUSAGE) pButtonBuffer;
ulResult = 0; // set to not found value
for(int i = 0; i < (long) Buttons; i++)
{
if(pUsage[i] == m_pProperties->m_Usage)
{
ulResult = 1;
break;
}
}
ExFreePool(pButtonBuffer);
}
m_Value = ulResult;
return TRUE;
break;
case eInputValue:
case eInputButton:
// have we had input data ?
if(!m_pHidDevice->m_pReadBuffer) break; // nope, leave
// do we have the report that contains this data?
ReportID = (BYTE) m_pProperties->m_ReportID;
if(!*m_pHidDevice->m_pReadBuffer == ReportID) break; // not us
if(m_eType == eInputValue)
{
ntStatus = HidP_GetUsageValue(
HidP_Input,
m_pProperties->m_UsagePage,
m_pProperties->m_LinkCollection,
m_pProperties->m_Usage,
&ulResult,
m_pHidDevice->m_pPreparsedData,
(char *)m_pHidDevice->m_pReadBuffer,
m_pHidDevice->m_pCaps->InputReportByteLength
);
if(NT_ERROR(ntStatus)) {
HidBattPrint(HIDBATT_DATA,("GetFeature - (Button) HidP_GetUsageValue 0x%08x\n", ntStatus));
return FALSE;
}
} else
{
// handle button
// must get button data
ULONG Buttons, BufferSize;
PUSAGE_AND_PAGE pButtonBuffer;
Buttons = HidP_MaxUsageListLength (HidP_Input, 0, m_pHidDevice->m_pPreparsedData);
BufferSize = Buttons * sizeof(USAGE_AND_PAGE);
pButtonBuffer = (PUSAGE_AND_PAGE)
ExAllocatePoolWithTag (NonPagedPool,
BufferSize,
HidBattTag);
RtlZeroMemory(pButtonBuffer,BufferSize);
ntStatus = HidP_GetButtons(
HidP_Input,
m_pProperties->m_UsagePage,
m_pProperties->m_LinkCollection,
(PUSAGE) pButtonBuffer,
&Buttons,
m_pHidDevice->m_pPreparsedData,
(char *) m_pHidDevice->m_pReadBuffer,
m_pHidDevice->m_pCaps->InputReportByteLength
);
if(!NT_SUCCESS(ntStatus))
{
HidBattPrint(HIDBATT_DATA,("GetFeature - (Button) HidP_GetButton 0x%08x\n", ntStatus));
// return error
return FALSE;
}
// get the value for the requested button
USAGE_AND_PAGE * UsagePage = (USAGE_AND_PAGE *) pButtonBuffer;
ulResult = 0; // set to not found value
for(int i = 0; i < (long) Buttons; i++)
{
if(UsagePage[i].Usage == m_pProperties->m_Usage)
// don't need to check usage page, because it was specified
// && UsagePage[i].UsagePage == m_pProperties->m_UsagePage)
{
ulResult = 1;
break;
}
}
ExFreePool(pButtonBuffer);
}
if(m_Value != ulResult)
{
m_Value = ulResult;
// there's been a change, check if this needs alerting
if(m_pProperties->m_pType->IsAlertable())
{
// call registered notification callback with
// ... passing the notificable usage object
if(m_pHidDevice->m_pEventHandler)
{
(*m_pHidDevice->m_pEventHandler)(
m_pHidDevice->m_pEventContext,
this);
}
}
}
return TRUE;
break;
}
return FALSE;
}
void CUsage::SetCapabilities()
{
// init capabilites from value caps and set properties for this usage
m_pProperties = new (NonPagedPool, HidBattTag) CProperties(this);
}
ULONG CUsage::GetUnit()
{
return m_pProperties->m_Unit;
}
SHORT CUsage::GetExponent()
{
return m_pProperties->m_UnitExponent;
}
NTSTATUS CUsage::GetString(char * pBuffer, USHORT buffLen, PULONG pBytesReturned)
{
char cBuffer[4];
ULONG StringIndex;
NTSTATUS ntStatus;
ULONG ulBytesWritten = 0;
bool bResult;
// first must update this usages value
bResult = GetValue();
if(!bResult) return STATUS_UNSUCCESSFUL;
RtlCopyMemory(cBuffer,&m_Value,sizeof(ULONG));
ntStatus = DoIoctl(
m_pHidDevice->m_pLowerDeviceObject,
IOCTL_HID_GET_INDEXED_STRING,
cBuffer,
4,
pBuffer,
buffLen,
m_pHidDevice);
return ntStatus;
}
// utility to do writefile for output reports
NTSTATUS HidWriteFile(
CHidDevice * pHidDevice,
PVOID pOutputBuffer,
USHORT usBufferLen,
PULONG pulBytesWritten
)
{
KEVENT WrittenEvent;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION pNewStack;
return STATUS_SUCCESS;
KeInitializeEvent(&WrittenEvent,NotificationEvent,FALSE);
// allocate write irp
PIRP pIrp = IoBuildSynchronousFsdRequest(
IRP_MJ_WRITE,
pHidDevice->m_pLowerDeviceObject,
pOutputBuffer,
usBufferLen,
0,
&WrittenEvent,
&IoStatusBlock
);
if(!pIrp) return STATUS_INSUFFICIENT_RESOURCES;
pNewStack= IoGetNextIrpStackLocation(pIrp);
pNewStack->FileObject = pHidDevice->m_pFCB;
NTSTATUS ntStatus = IoCallDriver(pHidDevice->m_pLowerDeviceObject,pIrp);
if(NT_ERROR(ntStatus))
{
IoFreeIrp(pIrp);
return ntStatus;
}
ntStatus = KeWaitForSingleObject(
&WrittenEvent,
Executive,
KernelMode,
FALSE,
NULL
);
IoFreeIrp(pIrp); // done with Irp
if(NT_ERROR(ntStatus))
{
return ntStatus;
}
*pulBytesWritten = (ULONG)IoStatusBlock.Information;
return IoStatusBlock.Status;
}
bool CUsage::SetValue(ULONG ulValue)
{
NTSTATUS ntStatus;
USHORT ThisType;
char * pOutputBuffer;
USHORT usBufferLen;
bool bResult;
ULONG ulBytesWritten;
HIDDebugBreak(HIDBATT_BREAK_NEVER);
// first check that this is an output or feature report
if(m_eType == eInputButton ||
m_eType == eInputValue)
{
// if this is an input usage, reject update
return FALSE;
}
if(m_eType == eOutputButton ||
m_eType == eOutputValue)
{
ThisType = HidP_Output;
usBufferLen = m_pHidDevice->m_pCaps->OutputReportByteLength;
} else
{
ThisType = HidP_Feature;
usBufferLen = m_pHidDevice->m_pCaps->FeatureReportByteLength + 1; // for report id
}
pOutputBuffer = (char *) ExAllocatePoolWithTag (NonPagedPool,
usBufferLen+1,
HidBattTag);
if (!pOutputBuffer) {
// Allocation failed
return FALSE;
}
pOutputBuffer[0] = (char) m_pProperties->m_ReportID;
// setup buffer for write
ntStatus = HidP_SetUsageValue(
(HIDP_REPORT_TYPE) ThisType, // either feature or output
m_pProperties->m_UsagePage,
m_pProperties->m_LinkCollection,
m_pProperties->m_Usage,
ulValue,
m_pHidDevice->m_pPreparsedData,
pOutputBuffer,
usBufferLen-1
);
if(ThisType == HidP_Output)
{
ntStatus = HidWriteFile(
m_pHidDevice,
pOutputBuffer,
usBufferLen,
&ulBytesWritten
);
} else
{
ntStatus = DoIoctl(
m_pHidDevice->m_pLowerDeviceObject,
IOCTL_HID_SET_FEATURE,
pOutputBuffer, //NULL,
usBufferLen, //0,
pOutputBuffer,
usBufferLen,
m_pHidDevice
);
}
ExFreePool (pOutputBuffer);
return ntStatus ? FALSE:TRUE;
}
// Usage Array Class
CUsageArray::CUsageArray()
{
m_UsageCount = 0;
m_pUsages = (CUsage **) NULL;
return;
}
CUsageArray::~CUsageArray()
{
while(m_UsageCount) {
delete m_pUsages[--m_UsageCount];
}
if (m_pUsages) {
ExFreePool (m_pUsages);
}
return;
}
void CUsageArray::AddUsage(CUsage * pNewUsage)
{
HIDDebugBreak(HIDBATT_BREAK_NEVER);
if(!m_pUsages)
{
m_pUsages = (CUsage **) ExAllocatePoolWithTag (NonPagedPool,
sizeof(CUsage *),
HidBattTag);
} else
{
CUsage ** pTemp = m_pUsages;
m_pUsages = (CUsage **) ExAllocatePoolWithTag (NonPagedPool,
sizeof(CUsage *) * (m_UsageCount + 1),
HidBattTag);
if (m_pUsages) {
memcpy(m_pUsages,pTemp,sizeof(CUsage *) * m_UsageCount);
ExFreePool(pTemp);
}
}
if (m_pUsages) {
m_pUsages[m_UsageCount] = pNewUsage;
m_UsageCount++;
}
// else - no way to report allocation failure. The only indication is that
// m_UsageCount is not increamented. (v-stebe)
}