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
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)
|
|
}
|
|
|