761 lines
22 KiB
C
761 lines
22 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pnp.c
|
|
|
|
Abstract: ESC/POS (serial) interface for USB Point-of-Sale devices
|
|
|
|
Author:
|
|
|
|
ervinp
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include <WDM.H>
|
|
|
|
#include <usbdi.h>
|
|
#include <usbdlib.h>
|
|
#include <usbioctl.h>
|
|
|
|
#include "escpos.h"
|
|
#include "debug.h"
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FDO_PnP)
|
|
#pragma alloc_text(PAGE, GetDeviceCapabilities)
|
|
#endif
|
|
|
|
|
|
NTSTATUS FDO_PnP(PARENTFDOEXT *parentFdoExt, PIRP irp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch routine for PnP IRPs (MajorFunction == IRP_MJ_PNP)
|
|
|
|
Arguments:
|
|
|
|
parentFdoExt - device extension for the targetted device object
|
|
irp - IO Request Packet
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOLEAN completeIrpHere = FALSE;
|
|
BOOLEAN justReturnStatus = FALSE;
|
|
ULONG minorFunction;
|
|
enum deviceState prevState;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
/*
|
|
* Get this field into a stack var so we can touch it after the IRP's completed.
|
|
*/
|
|
minorFunction = (ULONG)(irpSp->MinorFunction);
|
|
|
|
DBG_LOG_PNP_IRP(irp, minorFunction, FALSE, FALSE, -1);
|
|
|
|
switch (minorFunction){
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
prevState = parentFdoExt->state;
|
|
parentFdoExt->state = STATE_STARTING;
|
|
|
|
/*
|
|
* First, send the START_DEVICE irp down the stack
|
|
* synchronously to start the lower stack.
|
|
* We cannot do anything with our device object
|
|
* before propagating the START_DEVICE this way.
|
|
*/
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
status = CallNextDriverSync(parentFdoExt, irp);
|
|
|
|
if (NT_SUCCESS(status) && (prevState != STATE_STOPPED)){
|
|
/*
|
|
* Now that the lower stack is started,
|
|
* do any initialization required by this device object.
|
|
*/
|
|
status = GetDeviceCapabilities(parentFdoExt);
|
|
if (NT_SUCCESS(status)){
|
|
|
|
status = InitUSB(parentFdoExt);
|
|
if (NT_SUCCESS(status)){
|
|
/*
|
|
* Check whether any special feature needs to be implemented.
|
|
*/
|
|
DBGVERBOSE(("FDO_PnP: Poking registry for posFlag..."));
|
|
status = QuerySpecialFeature(parentFdoExt);
|
|
|
|
if (NT_SUCCESS(status)){
|
|
status = CreatePdoForEachEndpointPair(parentFdoExt);
|
|
if (NT_SUCCESS(status)){
|
|
IoInvalidateDeviceRelations(parentFdoExt->physicalDevObj, BusRelations);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)){
|
|
parentFdoExt->state = STATE_STARTED;
|
|
}
|
|
else {
|
|
parentFdoExt->state = STATE_START_FAILED;
|
|
}
|
|
completeIrpHere = TRUE;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
if (parentFdoExt->state == STATE_SUSPENDED){
|
|
status = STATUS_DEVICE_POWER_FAILURE;
|
|
completeIrpHere = TRUE;
|
|
}
|
|
else {
|
|
/*
|
|
* Only set state to STOPPED if the device was
|
|
* previously started successfully.
|
|
*/
|
|
if (parentFdoExt->state == STATE_STARTED){
|
|
parentFdoExt->state = STATE_STOPPED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
/*
|
|
* We will pass this IRP down the driver stack.
|
|
* However, we need to change the default status
|
|
* from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
|
|
*/
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
|
|
/*
|
|
* We will pass this IRP down the driver stack.
|
|
* However, we need to change the default status
|
|
* from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
|
|
*/
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
/*
|
|
* For now just set the STATE_REMOVING state so that
|
|
* we don't do any more IO. We are guaranteed to get
|
|
* IRP_MN_REMOVE_DEVICE soon; we'll do the rest of
|
|
* the remove processing there.
|
|
*/
|
|
parentFdoExt->state = STATE_REMOVING;
|
|
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
/*
|
|
* Check the current state to guard against multiple
|
|
* REMOVE_DEVICE IRPs.
|
|
*/
|
|
parentFdoExt->state = STATE_REMOVED;
|
|
|
|
|
|
/*
|
|
* Send the REMOVE IRP down the stack asynchronously.
|
|
* Do not synchronize sending down the REMOVE_DEVICE
|
|
* IRP, because the REMOVE_DEVICE IRP must be sent
|
|
* down and completed all the way back up to the sender
|
|
* before we continue.
|
|
*/
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
status = IoCallDriver(parentFdoExt->physicalDevObj, irp);
|
|
justReturnStatus = TRUE;
|
|
|
|
DBGVERBOSE(("REMOVE_DEVICE - waiting for %d irps to complete...", parentFdoExt->pendingActionCount));
|
|
|
|
/*
|
|
* We must for all outstanding IO to complete before
|
|
* completing the REMOVE_DEVICE IRP.
|
|
*
|
|
* First do an extra decrement on the pendingActionCount.
|
|
* This will cause pendingActionCount to eventually
|
|
* go to -1 once all asynchronous actions on this
|
|
* device object are complete.
|
|
* Then wait on the event that gets set when the
|
|
* pendingActionCount actually reaches -1.
|
|
*/
|
|
DecrementPendingActionCount(parentFdoExt);
|
|
KeWaitForSingleObject( &parentFdoExt->removeEvent,
|
|
Executive, // wait reason
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL ); // no timeout
|
|
|
|
DBGVERBOSE(("REMOVE_DEVICE - ... DONE waiting. "));
|
|
|
|
/*
|
|
* Detach our device object from the lower device object stack.
|
|
*/
|
|
IoDetachDevice(parentFdoExt->topDevObj);
|
|
|
|
/*
|
|
* Delete all child PDOs.
|
|
*/
|
|
if (ISPTR(parentFdoExt->deviceRelations)){
|
|
ULONG i;
|
|
|
|
DBGVERBOSE(("Parent deleting %xh child PDOs on REMOVE_DEVICE", parentFdoExt->deviceRelations->Count));
|
|
for (i = 0; i < parentFdoExt->deviceRelations->Count; i++){
|
|
PDEVICE_OBJECT childPdo = parentFdoExt->deviceRelations->Objects[i];
|
|
DEVEXT *devExt = childPdo->DeviceExtension;
|
|
POSPDOEXT *pdoExt;
|
|
|
|
ASSERT(devExt->signature == DEVICE_EXTENSION_SIGNATURE);
|
|
ASSERT(devExt->isPdo);
|
|
pdoExt = &devExt->pdoExt;
|
|
DeleteChildPdo(pdoExt);
|
|
}
|
|
FREEPOOL(parentFdoExt->deviceRelations);
|
|
parentFdoExt->deviceRelations = BAD_POINTER;
|
|
}
|
|
|
|
if (ISPTR(parentFdoExt->interfaceInfo)){
|
|
FREEPOOL(parentFdoExt->interfaceInfo);
|
|
}
|
|
|
|
if (ISPTR(parentFdoExt->configDesc)){
|
|
FREEPOOL(parentFdoExt->configDesc);
|
|
}
|
|
|
|
/*
|
|
* Delete our device object.
|
|
* This will also delete the associated device extension.
|
|
*/
|
|
IoDeleteDevice(parentFdoExt->functionDevObj);
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
if (irpSp->Parameters.QueryDeviceRelations.Type == BusRelations){
|
|
status = QueryDeviceRelations(parentFdoExt, irp);
|
|
if (NT_SUCCESS(status)){
|
|
/*
|
|
* Although we may have satisfied this IRP,
|
|
* we still pass it down the stack.
|
|
* But change the default status to success.
|
|
*/
|
|
irp->IoStatus.Status = status;
|
|
}
|
|
else {
|
|
completeIrpHere = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
/*
|
|
* Return the USB PDO's capabilities, but add the SurpriseRemovalOK bit.
|
|
*/
|
|
ASSERT(irpSp->Parameters.DeviceCapabilities.Capabilities);
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
status = CallNextDriverSync(parentFdoExt, irp);
|
|
if (NT_SUCCESS(status)){
|
|
irpSp->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
|
|
}
|
|
completeIrpHere = TRUE;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
if (justReturnStatus){
|
|
/*
|
|
* We've already sent this IRP down the stack asynchronously.
|
|
*/
|
|
}
|
|
else if (completeIrpHere){
|
|
ASSERT(status != NO_STATUS);
|
|
irp->IoStatus.Status = status;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
else {
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
status = IoCallDriver(parentFdoExt->physicalDevObj, irp);
|
|
}
|
|
|
|
DBG_LOG_PNP_IRP(irp, minorFunction, FALSE, TRUE, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS GetDeviceCapabilities(PARENTFDOEXT *parentFdoExt)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function retrieves the DEVICE_CAPABILITIES descriptor from the device
|
|
|
|
Arguments:
|
|
|
|
parentFdoExt - device extension for targetted device object
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PIRP irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
irp = IoAllocateIrp(parentFdoExt->physicalDevObj->StackSize, FALSE);
|
|
if (irp){
|
|
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp);
|
|
|
|
nextSp->MajorFunction = IRP_MJ_PNP;
|
|
nextSp->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
|
|
RtlZeroMemory( &parentFdoExt->deviceCapabilities,
|
|
sizeof(DEVICE_CAPABILITIES));
|
|
nextSp->Parameters.DeviceCapabilities.Capabilities =
|
|
&parentFdoExt->deviceCapabilities;
|
|
|
|
/*
|
|
* For any IRP you create, you must set the default status
|
|
* to STATUS_NOT_SUPPORTED before sending it.
|
|
*/
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
status = CallNextDriverSync(parentFdoExt, irp);
|
|
|
|
IoFreeIrp(irp);
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS QueryDeviceRelations(PARENTFDOEXT *parentFdoExt, PIRP irp)
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
if (irpSp->Parameters.QueryDeviceRelations.Type == BusRelations){
|
|
DBGVERBOSE(("QueryDeviceRelations(BusRelations) for parent"));
|
|
|
|
/*
|
|
* NTKERN expects a new pointer each time it calls QUERY_DEVICE_RELATIONS;
|
|
* it then FREES THE POINTER.
|
|
* So we have to return a new pointer each time, whether or not we actually
|
|
* created our copy of the device relations for this call.
|
|
*/
|
|
irp->IoStatus.Information = (ULONG)CopyDeviceRelations(parentFdoExt->deviceRelations);
|
|
if (irp->IoStatus.Information){
|
|
ULONG i;
|
|
|
|
/*
|
|
* The kernel dereferences each device object
|
|
* in the device relations list after each call.
|
|
* So for each call, add an extra reference.
|
|
*/
|
|
for (i = 0; i < parentFdoExt->deviceRelations->Count; i++){
|
|
ObReferenceObject(parentFdoExt->deviceRelations->Objects[i]);
|
|
parentFdoExt->deviceRelations->Objects[i]->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
}
|
|
|
|
DBGVERBOSE(("Returned %d child PDOs", parentFdoExt->deviceRelations->Count));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
else {
|
|
ASSERT(irpSp->Parameters.QueryDeviceRelations.Type == BusRelations);
|
|
status = irp->IoStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS PDO_PnP(POSPDOEXT *pdoExt, PIRP irp)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
DBG_LOG_PNP_IRP(irp, irpSp->MinorFunction, TRUE, FALSE, -1);
|
|
|
|
switch (irpSp->MinorFunction){
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
status = CreateSymbolicLink(pdoExt);
|
|
if (NT_SUCCESS(status)){
|
|
pdoExt->state = STATE_STARTED;
|
|
/*
|
|
* Initializing URB to read the status endpoint for Serial Emulation.
|
|
*/
|
|
if(pdoExt->parentFdoExt->posFlag & SERIAL_EMULATION) {
|
|
StatusPipe(pdoExt, pdoExt->statusEndpointInfo.pipeHandle);
|
|
DBGVERBOSE(("PDO_PnP: URB to read Status Endpoint initialized"));
|
|
}
|
|
}
|
|
else {
|
|
pdoExt->state = STATE_START_FAILED;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
pdoExt->state = STATE_STOPPED;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
status = FlushBuffers(pdoExt);
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
{
|
|
ULONG oldState = pdoExt->state;
|
|
KIRQL oldIrql;
|
|
BOOLEAN foundPdo = FALSE;
|
|
ULONG i;
|
|
|
|
|
|
pdoExt->state = STATE_REMOVED;
|
|
|
|
if ((oldState == STATE_STARTED) || (oldState == STATE_STOPPED)){
|
|
DestroySymbolicLink(pdoExt);
|
|
}
|
|
else {
|
|
DBGWARN(("previous state is %xh during pdo REMOVE_DEVICE", oldState));
|
|
}
|
|
|
|
|
|
/*
|
|
* See if this child PDO is still in the parent's device relations array.
|
|
*/
|
|
KeAcquireSpinLock(&pdoExt->parentFdoExt->devExtSpinLock, &oldIrql);
|
|
for (i = 0; i < pdoExt->parentFdoExt->deviceRelations->Count; i++){
|
|
if (pdoExt->parentFdoExt->deviceRelations->Objects[i] == pdoExt->pdo){
|
|
foundPdo = TRUE;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&pdoExt->parentFdoExt->devExtSpinLock, oldIrql);
|
|
|
|
FlushBuffers(pdoExt);
|
|
|
|
if (foundPdo){
|
|
/*
|
|
* If we are still in the parent's deviceRelations, then don't
|
|
* free the pdo or pdoName.Buffer . We may get another START
|
|
* on this same PDO.
|
|
*/
|
|
}
|
|
else {
|
|
|
|
/*
|
|
* This shouldn't happen.
|
|
*/
|
|
ASSERT(foundPdo);
|
|
|
|
/*
|
|
* We've recorded this COM port statically in our software key,
|
|
* so don't free it in the COM name arbiter.
|
|
*/
|
|
// ReleaseCOMPort(pdoExt->comPortNumber);
|
|
|
|
DeleteChildPdo(pdoExt);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_ID:
|
|
status = QueryPdoID(pdoExt, irp);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
ASSERT(irpSp->Parameters.DeviceCapabilities.Capabilities);
|
|
RtlCopyMemory( irpSp->Parameters.DeviceCapabilities.Capabilities,
|
|
&pdoExt->parentFdoExt->deviceCapabilities,
|
|
sizeof(DEVICE_CAPABILITIES));
|
|
|
|
/*
|
|
* Set the 'Raw' capability so that the child PDO gets started immediately,
|
|
* without anyone having to do an open on it first.
|
|
*/
|
|
irpSp->Parameters.DeviceCapabilities.Capabilities->RawDeviceOK = TRUE;
|
|
|
|
irpSp->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
status = CallNextDriverSync(pdoExt->parentFdoExt, irp);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
if (irpSp->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation){
|
|
/*
|
|
* Return a reference to this PDO
|
|
*/
|
|
PDEVICE_RELATIONS devRel = ALLOCPOOL(PagedPool, sizeof(DEVICE_RELATIONS));
|
|
if (devRel){
|
|
/*
|
|
* Add a reference to the PDO, since CONFIGMG will free it.
|
|
*/
|
|
ObReferenceObject(pdoExt->pdo);
|
|
devRel->Objects[0] = pdoExt->pdo;
|
|
devRel->Count = 1;
|
|
irp->IoStatus.Information = (ULONG_PTR)devRel;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* Fail this Irp by returning the default
|
|
* status (typically STATUS_NOT_SUPPORTED).
|
|
*/
|
|
DBGVERBOSE(("PDO_PnP: not handling QueryDeviceRelations type %xh.", (ULONG)irpSp->Parameters.QueryDeviceRelations.Type));
|
|
status = irp->IoStatus.Status;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Fail the IRP with the default status
|
|
*/
|
|
status = irp->IoStatus.Status;
|
|
break;
|
|
}
|
|
|
|
DBG_LOG_PNP_IRP(irp, irpSp->MinorFunction, TRUE, TRUE, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS SubstituteOneBusName(PWCHAR hwId)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG len = WStrLen(hwId);
|
|
|
|
if ((len > 4) && !WStrNCmpI(hwId, L"USB\\", 4)){
|
|
|
|
hwId[0] = L'P';
|
|
hwId[1] = L'O';
|
|
hwId[2] = L'S';
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
DBGERR(("SubstituteOneBusName: badly formed name at %ph.", hwId));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* SubstituteBusNames
|
|
*
|
|
* busNames is a multi-string of PnP ids.
|
|
* Subsitute 'POS\' for 'USB\' in each one.
|
|
*/
|
|
NTSTATUS SubstituteBusNames(PWCHAR busNames)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
while (*busNames && (status == STATUS_SUCCESS)){
|
|
ULONG len = WStrLen(busNames);
|
|
status = SubstituteOneBusName(busNames);
|
|
busNames += (len+1);
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS QueryPdoID(POSPDOEXT *pdoExt, PIRP irp)
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
DBGVERBOSE((" QueryPdoId: idType = %xh ", irpSp->Parameters.QueryId.IdType));
|
|
|
|
switch (irpSp->Parameters.QueryId.IdType){
|
|
|
|
case BusQueryHardwareIDs:
|
|
|
|
/*
|
|
* Return a multi-string containing the PnP ID for the POS serial interface.
|
|
* Must terminate multi-string with a double unicode null.
|
|
*/
|
|
(PWCHAR)irp->IoStatus.Information =
|
|
MemDup(L"POS\\POS_SERIAL_INTERFACE\0", sizeof(L"POS\\POS_SERIAL_INTERFACE\0"));
|
|
if (irp->IoStatus.Information){
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ASSERT(irp->IoStatus.Information);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
|
|
case BusQueryDeviceID:
|
|
/*
|
|
* Return the PnP ID for the POS serial interface.
|
|
*/
|
|
(PWCHAR)irp->IoStatus.Information =
|
|
MemDup(L"POS\\POS_SERIAL_INTERFACE", sizeof(L"POS\\POS_SERIAL_INTERFACE"));
|
|
if (irp->IoStatus.Information){
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ASSERT(irp->IoStatus.Information);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
|
|
case BusQueryInstanceID:
|
|
|
|
/*
|
|
* Produce an instance-id for this function-PDO.
|
|
*
|
|
* Note: NTKERN frees the returned pointer, so we must provide a fresh pointer.
|
|
*/
|
|
{
|
|
PWSTR instanceId = MemDup(L"0000", sizeof(L"0000"));
|
|
if (instanceId){
|
|
KIRQL oldIrql;
|
|
ULONG i;
|
|
|
|
/*
|
|
* Find this collection-PDO in the device-relations array
|
|
* and make the id be the PDO's index within that array.
|
|
*/
|
|
KeAcquireSpinLock(&pdoExt->parentFdoExt->devExtSpinLock, &oldIrql);
|
|
for (i = 0; i < pdoExt->parentFdoExt->deviceRelations->Count; i++){
|
|
if (pdoExt->parentFdoExt->deviceRelations->Objects[i] == pdoExt->pdo){
|
|
break;
|
|
}
|
|
}
|
|
if (i < pdoExt->parentFdoExt->deviceRelations->Count){
|
|
NumToDecString(instanceId, (USHORT)i, 4);
|
|
irp->IoStatus.Information = (ULONG)instanceId;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ASSERT(i < pdoExt->parentFdoExt->deviceRelations->Count);
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
}
|
|
KeReleaseSpinLock(&pdoExt->parentFdoExt->devExtSpinLock, oldIrql);
|
|
|
|
if (!NT_SUCCESS(status)){
|
|
FREEPOOL(instanceId);
|
|
}
|
|
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
break;
|
|
|
|
case BusQueryCompatibleIDs:
|
|
/*
|
|
* Return multi-string of compatible IDs.
|
|
*/
|
|
(PWCHAR)irp->IoStatus.Information = MemDup(L"\0\0", sizeof(L"\0\0"));
|
|
if (irp->IoStatus.Information) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ASSERT(irp->IoStatus.Information);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Do not return STATUS_NOT_SUPPORTED;
|
|
* keep the default status
|
|
* (this allows filter drivers to work).
|
|
*/
|
|
status = irp->IoStatus.Status;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID DeleteChildPdo(POSPDOEXT *pdoExt)
|
|
{
|
|
ASSERT(pdoExt->pdoName.Buffer);
|
|
FREEPOOL(pdoExt->pdoName.Buffer);
|
|
IoDeleteDevice(pdoExt->pdo);
|
|
}
|
|
|
|
|
|
|