402 lines
12 KiB
C
402 lines
12 KiB
C
/*
|
|
*************************************************************************
|
|
* File: DISPATCH.C
|
|
*
|
|
* Module: USBCCGP.SYS
|
|
* USB Common Class Generic Parent driver.
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
*
|
|
* Author: ervinp
|
|
*
|
|
*************************************************************************
|
|
*/
|
|
|
|
#include <wdm.h>
|
|
#include <windef.h>
|
|
#include <unknown.h>
|
|
#ifdef DRM_SUPPORT
|
|
#include <ks.h>
|
|
#include <ksmedia.h>
|
|
#include <drmk.h>
|
|
#include <ksdrmhlp.h>
|
|
#endif
|
|
#include <usbdi.h>
|
|
#include <usbdlib.h>
|
|
#include <usbioctl.h>
|
|
|
|
#include "usbccgp.h"
|
|
#include "debug.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, USBC_Create)
|
|
#pragma alloc_text(PAGE, USBC_DeviceControl)
|
|
#pragma alloc_text(PAGE, USBC_SystemControl)
|
|
#pragma alloc_text(PAGE, USBC_Power)
|
|
#ifdef DRM_SUPPORT
|
|
#pragma alloc_text(PAGE, USBC_SetContentId)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/*
|
|
* USBC_Dispatch
|
|
*
|
|
* Note: this function cannot be pageable because reads can
|
|
* come in at DISPATCH level.
|
|
*/
|
|
NTSTATUS USBC_Dispatch(IN PDEVICE_OBJECT devObj, IN PIRP irp)
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVEXT devExt;
|
|
PPARENT_FDO_EXT parentFdoExt;
|
|
PFUNCTION_PDO_EXT functionPdoExt;
|
|
ULONG majorFunction, minorFunction;
|
|
BOOLEAN isParentFdo;
|
|
NTSTATUS status;
|
|
BOOLEAN abortIrp = FALSE;
|
|
|
|
devExt = devObj->DeviceExtension;
|
|
ASSERT(devExt);
|
|
ASSERT(devExt->signature == USBCCGP_TAG);
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
/*
|
|
* Keep these privately so we still have it after the IRP completes
|
|
* or after the device extension is freed on a REMOVE_DEVICE
|
|
*/
|
|
majorFunction = irpSp->MajorFunction;
|
|
minorFunction = irpSp->MinorFunction;
|
|
isParentFdo = devExt->isParentFdo;
|
|
|
|
DBG_LOG_IRP_MAJOR(irp, majorFunction, isParentFdo, FALSE, 0);
|
|
|
|
if (isParentFdo){
|
|
parentFdoExt = &devExt->parentFdoExt;
|
|
functionPdoExt = BAD_POINTER;
|
|
}
|
|
else {
|
|
functionPdoExt = &devExt->functionPdoExt;
|
|
parentFdoExt = functionPdoExt->parentFdoExt;
|
|
}
|
|
|
|
/*
|
|
* For all IRPs except REMOVE, we increment the PendingActionCount
|
|
* across the dispatch routine in order to prevent a race condition with
|
|
* the REMOVE_DEVICE IRP (without this increment, if REMOVE_DEVICE
|
|
* preempted another IRP, device object and extension might get
|
|
* freed while the second thread was still using it).
|
|
*/
|
|
if (!((majorFunction == IRP_MJ_PNP) && (minorFunction == IRP_MN_REMOVE_DEVICE))){
|
|
IncrementPendingActionCount(parentFdoExt);
|
|
}
|
|
|
|
|
|
/*
|
|
* Make sure we don't process any IRPs besides PNP and CLOSE
|
|
* while a device object is getting removed.
|
|
* Do this after we've incremented the pendingActionCount for this IRP.
|
|
*/
|
|
if ((majorFunction != IRP_MJ_PNP) && (majorFunction != IRP_MJ_CLOSE)){
|
|
enum deviceState state = (isParentFdo) ? parentFdoExt->state : functionPdoExt->state;
|
|
if (!isParentFdo && majorFunction == IRP_MJ_POWER) {
|
|
/*
|
|
* Don't abort power IRP's on child function PDO's, even if
|
|
* state is STATE_REMOVING or STATE_REMOVED as this will veto
|
|
* a suspend request if the child function PDO is disabled.
|
|
*/
|
|
;
|
|
} else if ((state == STATE_REMOVING) || (state == STATE_REMOVED)){
|
|
abortIrp = TRUE;
|
|
}
|
|
}
|
|
|
|
if (abortIrp){
|
|
/*
|
|
* Fail all irps after a remove irp.
|
|
* This should never happen except:
|
|
* we can get a power irp on a function pdo after a remove
|
|
* because (per splante) the power state machine is not synchronized
|
|
* with the pnp state machine. We now handle this case above.
|
|
*/
|
|
DBGWARN(("Aborting IRP %ph (function %xh/%xh) because delete pending", irp, majorFunction, minorFunction));
|
|
ASSERT((majorFunction == IRP_MJ_POWER) && !isParentFdo);
|
|
status = irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
|
if (majorFunction == IRP_MJ_POWER){
|
|
PoStartNextPowerIrp(irp);
|
|
}
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
else {
|
|
switch (majorFunction){
|
|
|
|
case IRP_MJ_CREATE:
|
|
status = USBC_Create(devExt, irp);
|
|
break;
|
|
|
|
case IRP_MJ_CLOSE:
|
|
status = USBC_Close(devExt, irp);
|
|
break;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
status = USBC_DeviceControl(devExt, irp);
|
|
break;
|
|
|
|
case IRP_MJ_SYSTEM_CONTROL:
|
|
status = USBC_SystemControl(devExt, irp);
|
|
break;
|
|
|
|
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
|
|
status = USBC_InternalDeviceControl(devExt, irp);
|
|
break;
|
|
|
|
case IRP_MJ_PNP:
|
|
status = USBC_PnP(devExt, irp);
|
|
break;
|
|
|
|
case IRP_MJ_POWER:
|
|
status = USBC_Power(devExt, irp);
|
|
break;
|
|
|
|
default:
|
|
DBGERR(("USBC_Dispatch: unsupported irp majorFunction %xh.", majorFunction));
|
|
if (isParentFdo){
|
|
/*
|
|
* Pass this IRP to the parent device.
|
|
*/
|
|
IoSkipCurrentIrpStackLocation(irp);
|
|
status = IoCallDriver(parentFdoExt->topDevObj, irp);
|
|
}
|
|
else {
|
|
/*
|
|
* This is not a pnp/power/syscntrl irp, so we fail unsupported irps
|
|
* with an actual error code (not with the default status).
|
|
*/
|
|
status = irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
DBG_LOG_IRP_MAJOR(irp, majorFunction, isParentFdo, TRUE, status);
|
|
|
|
/*
|
|
* Balance the increment above
|
|
*/
|
|
if (!((majorFunction == IRP_MJ_PNP) && (minorFunction == IRP_MN_REMOVE_DEVICE))){
|
|
DecrementPendingActionCount(parentFdoExt);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS USBC_Create(PDEVEXT devExt, PIRP irp)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (devExt->isParentFdo){
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
IoSkipCurrentIrpStackLocation(irp);
|
|
status = IoCallDriver(parentFdoExt->topDevObj, irp);
|
|
}
|
|
else {
|
|
/*
|
|
* This is not a pnp/power/syscntrl irp, so we fail unsupported irps
|
|
* with an actual error code (not with the default status).
|
|
*/
|
|
status = irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS USBC_Close(PDEVEXT devExt, PIRP irp)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if (devExt->isParentFdo){
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
IoSkipCurrentIrpStackLocation(irp);
|
|
status = IoCallDriver(parentFdoExt->topDevObj, irp);
|
|
}
|
|
else {
|
|
/*
|
|
* This is not a pnp/power/syscntrl irp, so we fail unsupported irps
|
|
* with an actual error code (not with the default status).
|
|
*/
|
|
status = irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
#ifdef DRM_SUPPORT
|
|
|
|
/*****************************************************************************
|
|
* USBC_SetContentId
|
|
*****************************************************************************
|
|
*
|
|
*/
|
|
NTSTATUS
|
|
USBC_SetContentId
|
|
(
|
|
IN PIRP irp,
|
|
IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
|
|
IN PKSDRMAUDIOSTREAM_CONTENTID pvData
|
|
)
|
|
{
|
|
ULONG ContentId;
|
|
PIO_STACK_LOCATION iostack;
|
|
PDEVEXT devExt;
|
|
USBD_PIPE_HANDLE hPipe;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(irp);
|
|
ASSERT(pKsProperty);
|
|
ASSERT(pvData);
|
|
|
|
iostack = IoGetCurrentIrpStackLocation(irp);
|
|
devExt = iostack->DeviceObject->DeviceExtension;
|
|
hPipe = pKsProperty->Context;
|
|
ContentId = pvData->ContentId;
|
|
|
|
if (devExt->isParentFdo){
|
|
// IOCTL sent to parent FDO. Forward to down the stack.
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
status = pKsProperty->DrmForwardContentToDeviceObject(ContentId, parentFdoExt->topDevObj, hPipe);
|
|
}
|
|
else {
|
|
// IOCTL send to function PDO. Forward to parent FDO on other stack.
|
|
PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt;
|
|
PPARENT_FDO_EXT parentFdoExt = functionPdoExt->parentFdoExt;
|
|
status = pKsProperty->DrmForwardContentToDeviceObject(ContentId, parentFdoExt->fdo, hPipe);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
NTSTATUS USBC_DeviceControl(PDEVEXT devExt, PIRP irp)
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
ULONG ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
#ifdef DRM_SUPPORT
|
|
|
|
if (IOCTL_KS_PROPERTY == ioControlCode) {
|
|
status = KsPropertyHandleDrmSetContentId(irp, USBC_SetContentId);
|
|
irp->IoStatus.Status = status;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
} else {
|
|
#endif
|
|
if (devExt->isParentFdo){
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
status = ParentDeviceControl(parentFdoExt, irp);
|
|
}
|
|
else {
|
|
/*
|
|
* Pass the IOCTL IRP sent to our child PDO to our own parent FDO.
|
|
*/
|
|
PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt;
|
|
PPARENT_FDO_EXT parentFdoExt = functionPdoExt->parentFdoExt;
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
status = IoCallDriver(parentFdoExt->fdo, irp);
|
|
}
|
|
#ifdef DRM_SUPPORT
|
|
}
|
|
#endif
|
|
|
|
DBG_LOG_IOCTL(ioControlCode, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS USBC_SystemControl(PDEVEXT devExt, PIRP irp)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (devExt->isParentFdo){
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
IoSkipCurrentIrpStackLocation(irp);
|
|
status = IoCallDriver(parentFdoExt->topDevObj, irp);
|
|
}
|
|
else {
|
|
/*
|
|
* Pass the IOCTL IRP sent to our child PDO to our own parent FDO.
|
|
*/
|
|
PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt;
|
|
PPARENT_FDO_EXT parentFdoExt = functionPdoExt->parentFdoExt;
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
status = IoCallDriver(parentFdoExt->fdo, irp);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* USBC_InternalDeviceControl
|
|
*
|
|
*
|
|
* Note: this function cannot be pageable because internal
|
|
* ioctls may be sent at IRQL==DISPATCH_LEVEL.
|
|
*/
|
|
NTSTATUS USBC_InternalDeviceControl(PDEVEXT devExt, PIRP irp)
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
ULONG ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
|
|
NTSTATUS status;
|
|
|
|
if (devExt->isParentFdo){
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
status = ParentInternalDeviceControl(parentFdoExt, irp);
|
|
}
|
|
else {
|
|
PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt;
|
|
status = FunctionInternalDeviceControl(functionPdoExt, irp);
|
|
}
|
|
|
|
DBG_LOG_IOCTL(ioControlCode, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS USBC_Power(PDEVEXT devExt, PIRP irp)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (devExt->isParentFdo){
|
|
PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
|
|
status = HandleParentFdoPower(parentFdoExt, irp);
|
|
}
|
|
else {
|
|
PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt;
|
|
status = HandleFunctionPdoPower(functionPdoExt, irp);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|