windows-nt/Source/XPSP1/NT/drivers/wdm/input/hidclass/name.c

468 lines
16 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
name.c
Abstract
Get-friendly-name handling routines
Author:
Ervin P.
Environment:
Kernel mode only
Revision History:
--*/
#include "pch.h"
/*
********************************************************************************
* HidpGetDeviceString
********************************************************************************
*
* Note: This function cannot be pageable because it is called
* from the IOCTL dispatch routine, which can get called
* at DISPATCH_LEVEL.
*
*/
NTSTATUS HidpGetDeviceString(IN FDO_EXTENSION *fdoExt,
IN OUT PIRP Irp,
IN ULONG stringId,
IN ULONG languageId)
{
BOOLEAN completeIrpHere = TRUE;
NTSTATUS status;
PIO_STACK_LOCATION currentIrpSp, nextIrpSp;
currentIrpSp = IoGetCurrentIrpStackLocation(Irp);
nextIrpSp = IoGetNextIrpStackLocation(Irp);
/*
* The Irp we got uses buffering type METHOD_OUT_DIRECT,
* which passes the buffer in the MDL.
* IOCTL_HID_GET_STRING uses buffering type METHOD_NEITHER,
* which passes the buffer in Irp->UserBuffer.
* So we have to copy the pointer.
*/
Irp->UserBuffer = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
if (Irp->UserBuffer){
/*
* Prepare the next (lower) IRP stack location.
* This will be the minidriver's (e.g. HIDUSB's) "current" stack location.
*/
nextIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextIrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_STRING;
nextIrpSp->Parameters.DeviceIoControl.OutputBufferLength =
currentIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
// Type3InputBuffer has string/lang IDs
nextIrpSp->Parameters.DeviceIoControl.Type3InputBuffer =
ULongToPtr((ULONG)(stringId + (languageId << 16)));
status = HidpCallDriver(fdoExt->fdo, Irp);
/*
* Irp will be completed by lower driver
*/
completeIrpHere = FALSE;
}
else {
status = STATUS_INVALID_USER_BUFFER;
}
if (completeIrpHere){
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
DBGSUCCESS(status, FALSE)
return status;
}
/*
********************************************************************************
* HidpGetIndexedString
********************************************************************************
*
*
*
*/
NTSTATUS HidpGetIndexedString( IN FDO_EXTENSION *fdoExt,
IN OUT PIRP Irp,
IN ULONG stringIndex,
IN ULONG languageId)
{
NTSTATUS status;
PIO_STACK_LOCATION currentIrpSp, nextIrpSp;
currentIrpSp = IoGetCurrentIrpStackLocation(Irp);
nextIrpSp = IoGetNextIrpStackLocation(Irp);
/*
* The Irp we got uses buffering type METHOD_OUT_DIRECT,
* which passes the buffer in the MDL.
* The Irp we're sending down uses the same buffering method,
* so just let the lower driver derive the system address
* from the MDL.
*/
/*
* Prepare the next (lower) IRP stack location.
* This will be the minidriver's (e.g. HIDUSB's) "current" stack location.
*/
nextIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextIrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_INDEXED_STRING;
nextIrpSp->Parameters.DeviceIoControl.OutputBufferLength = currentIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
// Type3InputBuffer has string index/lang IDs
nextIrpSp->Parameters.DeviceIoControl.Type3InputBuffer =
ULongToPtr((ULONG)(stringIndex + (languageId << 16)));
status = HidpCallDriver(fdoExt->fdo, Irp);
DBGSUCCESS(status, FALSE)
return status;
}
/*
********************************************************************************
* HidpGetMsGenreDescriptor
********************************************************************************
*
*
*
*/
NTSTATUS HidpGetMsGenreDescriptor(
IN FDO_EXTENSION *fdoExt,
IN OUT PIRP Irp)
{
NTSTATUS status;
PIO_STACK_LOCATION currentIrpSp, nextIrpSp;
DBGOUT(("Received request for genre descriptor in hidclass"))
currentIrpSp = IoGetCurrentIrpStackLocation(Irp);
nextIrpSp = IoGetNextIrpStackLocation(Irp);
/*
* The Irp we got uses buffering type METHOD_OUT_DIRECT,
* which passes the buffer in the MDL.
* The Irp we're sending down uses the same buffering method,
* so just let the lower driver derive the system address
* from the MDL.
*/
/*
* Prepare the next (lower) IRP stack location.
* This will be the minidriver's (e.g. HIDUSB's) "current" stack location.
*/
nextIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextIrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_MS_GENRE_DESCRIPTOR;
nextIrpSp->Parameters.DeviceIoControl.OutputBufferLength = currentIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
status = HidpCallDriver(fdoExt->fdo, Irp);
DBGSUCCESS(status, FALSE)
return status;
}
/*
********************************************************************************
* HidpGetSetReport
********************************************************************************
*
* There are not many differences between reading and writing a
* report at this level, whether it be an input, output or feature
* report, so we have one function do all six.
*
* controlCode is one of:
* IOCTL_HID_GET_INPUT_REPORT, IOCTL_HID_SET_INPUT_REPORT
* IOCTL_HID_GET_OUTPUT_REPORT, IOCTL_HID_SET_OUTPUT_REPORT
* IOCTL_HID_GET_FEATURE, IOCTL_HID_SET_FEATURE
*
* Note: This function cannot be pageable because it is called
* from the IOCTL dispatch routine, which can get called
* at DISPATCH_LEVEL.
*
*/
NTSTATUS HidpGetSetReport ( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
IN OUT PIRP Irp,
IN ULONG controlCode,
OUT BOOLEAN *sentIrp)
{
FDO_EXTENSION *fdoExt;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION currentIrpSp, nextIrpSp;
PFILE_OBJECT fileObject;
PHIDCLASS_FILE_EXTENSION fileExtension;
DBG_COMMON_ENTRY()
ASSERT(HidDeviceExtension->isClientPdo);
fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
currentIrpSp = IoGetCurrentIrpStackLocation(Irp);
nextIrpSp = IoGetNextIrpStackLocation(Irp);
*sentIrp = FALSE;
/*
* Get the file extension.
*/
ASSERT(currentIrpSp->FileObject);
fileObject = currentIrpSp->FileObject;
if(!fileObject->FsContext) {
DBGWARN(("Attempted to get/set report with no file extension"))
return STATUS_PRIVILEGE_NOT_HELD;
}
fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext;
ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
/*
* Check security.
*/
if (fileExtension->SecurityCheck){
PHIDP_COLLECTION_DESC collectionDesc;
/*
* Get our collection description.
*/
collectionDesc = GetCollectionDesc(fdoExt, fileExtension->CollectionNumber);
if (collectionDesc){
PUCHAR reportBuf;
ULONG reportBufLen;
BOOLEAN featureRequest = FALSE;
switch (controlCode){
case IOCTL_HID_GET_INPUT_REPORT:
// Make sure that there is an input report on this collection.
if (collectionDesc->InputLength == 0) {
DBGWARN(("No input report on collection %x",
fileExtension->CollectionNumber))
status = STATUS_INVALID_DEVICE_REQUEST;
}
break;
case IOCTL_HID_SET_OUTPUT_REPORT:
// Make sure that there is an output report on this collection.
if (collectionDesc->OutputLength == 0){
DBGWARN(("No output report on collection %x",
fileExtension->CollectionNumber))
status = STATUS_INVALID_DEVICE_REQUEST;
}
break;
case IOCTL_HID_GET_FEATURE:
case IOCTL_HID_SET_FEATURE:
featureRequest = TRUE;
// Make sure that there is a feature report on this collection.
if ((collectionDesc->FeatureLength == 0) &&
!(fdoExt->deviceSpecificFlags & DEVICE_FLAG_ALLOW_FEATURE_ON_NON_FEATURE_COLLECTION)){
DBGWARN(("No feature report on collection %x",
fileExtension->CollectionNumber))
status = STATUS_INVALID_DEVICE_REQUEST;
}
break;
default:
TRAP;
status = STATUS_INVALID_DEVICE_REQUEST;
}
switch (controlCode){
case IOCTL_HID_GET_INPUT_REPORT:
case IOCTL_HID_GET_FEATURE:
reportBufLen = currentIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
reportBuf = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
break;
case IOCTL_HID_SET_OUTPUT_REPORT:
case IOCTL_HID_SET_FEATURE:
reportBuf = Irp->AssociatedIrp.SystemBuffer;
reportBufLen = currentIrpSp->Parameters.DeviceIoControl.InputBufferLength;
break;
default:
TRAP;
status = STATUS_INVALID_PARAMETER;
reportBuf = NULL;
reportBufLen = 0;
}
if (reportBuf && reportBufLen && NT_SUCCESS(status)){
PHIDP_REPORT_IDS reportIdent;
UCHAR reportId;
/*
* The client includes the report id as the first byte of the report.
* We send down the report byte only if the device has multiple
* report IDs (i.e. the report id is not implicit).
*/
reportId = reportBuf[0];
if (fdoExt->deviceDesc.ReportIDs[0].ReportID == 0){
DBGASSERT((reportId == 0),
("Report Id should be zero, acutal id = %d", reportId),
FALSE)
reportBuf++;
reportBufLen--;
}
/*
* Find a matching report identifier.
*/
reportIdent = GetReportIdentifier(fdoExt, reportId);
/*
* Check the buffer length against the
* report length in the report identifier.
*/
if (reportIdent){
switch (controlCode){
case IOCTL_HID_GET_INPUT_REPORT:
/*
* The buffer must be big enough for the report.
*/
if (!reportIdent->InputLength ||
reportBufLen < reportIdent->InputLength){
ASSERT(!(PVOID)"report buf must be at least report size for get-report.");
reportIdent = NULL;
}
break;
case IOCTL_HID_GET_FEATURE:
/*
* The buffer must be big enough for the report.
*/
if (!reportIdent->FeatureLength ||
reportBufLen < reportIdent->FeatureLength){
ASSERT(!(PVOID)"report buf must be at least report size for get-report.");
reportIdent = NULL;
}
break;
case IOCTL_HID_SET_OUTPUT_REPORT:
/*
* The buffer must be big enough for the report.
* It CAN be larger, and it is up to us to use
* the correct report size from the report identifier.
*/
if (!reportIdent->OutputLength ||
reportBufLen < reportIdent->OutputLength){
ASSERT(!(PVOID)"report buf must be exact size for set-report.");
reportIdent = NULL;
} else {
reportBufLen = reportIdent->OutputLength;
}
break;
case IOCTL_HID_SET_FEATURE:
if (!reportIdent->FeatureLength ||
reportBufLen < reportIdent->FeatureLength){
ASSERT(!(PVOID)"report buf must be exact size for set-report.");
reportIdent = NULL;
} else {
reportBufLen = reportIdent->FeatureLength;
}
break;
default:
TRAP;
}
}
if (reportIdent ||
(featureRequest &&
(fdoExt->deviceSpecificFlags &
DEVICE_FLAG_ALLOW_FEATURE_ON_NON_FEATURE_COLLECTION))){
PHID_XFER_PACKET reportPacket = ALLOCATEPOOL(NonPagedPool, sizeof(HID_XFER_PACKET));
if (reportPacket){
reportPacket->reportBuffer = reportBuf;
reportPacket->reportBufferLen = reportBufLen;
reportPacket->reportId = reportId;
Irp->UserBuffer = reportPacket;
/*
* Prepare the next (lower) IRP stack location.
* This will be HIDUSB's "current" stack location.
*/
nextIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextIrpSp->Parameters.DeviceIoControl.IoControlCode = controlCode;
/*
* Note - input/output is relative to IOCTL servicer
*/
switch (controlCode){
case IOCTL_HID_GET_INPUT_REPORT:
case IOCTL_HID_GET_FEATURE:
nextIrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_XFER_PACKET);
break;
case IOCTL_HID_SET_OUTPUT_REPORT:
case IOCTL_HID_SET_FEATURE:
nextIrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(HID_XFER_PACKET);
break;
default:
TRAP;
}
DBG_RECORD_REPORT(reportId, controlCode, FALSE)
status = HidpCallDriverSynchronous(fdoExt->fdo, Irp);
if (!NT_SUCCESS(status)){
DBGWARN(("HidpGetSetFeature: usb returned status %xh.", status))
}
DBG_RECORD_REPORT(reportId, controlCode, TRUE)
ExFreePool(reportPacket);
*sentIrp = FALSE; // needs to be completed again
}
else {
ASSERT(reportPacket);
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else {
DBGASSERT(reportIdent, ("Some yahoo sent invalid data in ioctl %x", controlCode), FALSE)
status = STATUS_DATA_ERROR;
}
}
else if (NT_SUCCESS(status)) {
DBGASSERT(reportBuf, ("Feature buffer is invalid"), FALSE)
DBGASSERT(reportBufLen, ("Feature buffer length is invalid"), FALSE)
status = STATUS_INVALID_BUFFER_SIZE;
}
}
else {
ASSERT(collectionDesc);
status = STATUS_DEVICE_NOT_CONNECTED;
}
}
else {
ASSERT(fileExtension->SecurityCheck);
status = STATUS_PRIVILEGE_NOT_HELD;
}
DBGSUCCESS(status, FALSE)
DBG_COMMON_EXIT()
return status;
}