windows-nt/Source/XPSP1/NT/drivers/wdm/usb/usbccgp/urbfunc.c
2020-09-26 16:20:57 +08:00

218 lines
7.9 KiB
C

/*
*************************************************************************
* File: URBFUNC.C
*
* Module: USBCCGP.SYS
* USB Common Class Generic Parent driver.
*
* Copyright (c) 1998 Microsoft Corporation
*
*
* Author: ervinp
*
*************************************************************************
*/
#include <wdm.h>
#include <stdio.h>
#include <usbdi.h>
#include <usbdlib.h>
#include <usbioctl.h>
#include "usbccgp.h"
#include "debug.h"
/*
* UrbFunctionSelectConfiguration
*
*
*/
NTSTATUS UrbFunctionSelectConfiguration(PFUNCTION_PDO_EXT functionPdoExt, PURB urb)
{
NTSTATUS status = NO_STATUS;
if (urb->UrbSelectConfiguration.ConfigurationDescriptor){
PUSBD_INTERFACE_INFORMATION urbIface = &urb->UrbSelectConfiguration.Interface;
PUSBD_INTERFACE_LIST_ENTRY iface, funcIface = NULL;
ULONG i;
ASSERT(ISPTR(functionPdoExt->functionInterfaceList));
iface = functionPdoExt->functionInterfaceList;
for (i = 0; i < functionPdoExt->numInterfaces; i++){
if (iface->Interface->InterfaceNumber == urbIface->InterfaceNumber){
funcIface = iface;
break;
}
iface++;
}
if (funcIface && funcIface->Interface){
BOOLEAN sendSelectIface = FALSE;
BOOLEAN selectAltIface = FALSE;
/*
* To service the client's SELECT_CONFIGURATION call, we only need to
* call the parent if the client is:
* 1. Selecting a different alternate interface
* or
* 2. Changing the MaximumTransferSize for one of the pipes.
*
* In either of those cases, we send down a SELECT_INTERFACE request.
*/
if (funcIface->Interface->AlternateSetting != urbIface->AlternateSetting){
DBGWARN(("Coverage: Changing alt iface in UrbFunctionSelectConfiguration (iface #%xh from %xh to %xh).", urbIface->InterfaceNumber, funcIface->Interface->AlternateSetting, urbIface->AlternateSetting));
sendSelectIface = TRUE;
selectAltIface = TRUE;
}
else {
ULONG numPipes;
//
// We shouldn't be looking at NumberOfPipes in the URB because this is an
// OUTPUT field.
// ASSERT(urbIface->NumberOfPipes == funcIface->Interface->NumberOfPipes);
// numPipes = MIN(urbIface->NumberOfPipes, funcIface->Interface->NumberOfPipes);
numPipes = funcIface->Interface->NumberOfPipes;
for (i = 0; i < numPipes; i++){
if (urbIface->Pipes[i].MaximumTransferSize != funcIface->Interface->Pipes[i].MaximumTransferSize){
DBGWARN(("Coverage: Changing MaximumTransferSize in UrbFunctionSelectConfiguration (from %xh to %xh).", funcIface->Interface->Pipes[i].MaximumTransferSize, urbIface->Pipes[i].MaximumTransferSize));
sendSelectIface = TRUE;
}
}
}
if (sendSelectIface){
PURB selectIfaceUrb;
USHORT size;
//
// BUT, when choosing an alternate interface, we must use the NumberOfPipes in
// the URB.
//
if (selectAltIface){
size = (USHORT)(GET_SELECT_INTERFACE_REQUEST_SIZE(urbIface->NumberOfPipes));
}
else {
size = (USHORT)(GET_SELECT_INTERFACE_REQUEST_SIZE(funcIface->Interface->NumberOfPipes));
}
selectIfaceUrb = ALLOCPOOL(NonPagedPool, size);
if (selectIfaceUrb){
PUSBD_INTERFACE_INFORMATION selectIface = &selectIfaceUrb->UrbSelectInterface.Interface;
selectIfaceUrb->UrbSelectInterface.Hdr.Function = URB_FUNCTION_SELECT_INTERFACE;
selectIfaceUrb->UrbSelectInterface.Hdr.Length = size;
ASSERT(functionPdoExt->parentFdoExt->selectedConfigHandle);
selectIfaceUrb->UrbSelectInterface.ConfigurationHandle = functionPdoExt->parentFdoExt->selectedConfigHandle;
RtlCopyMemory(selectIface, urbIface, urbIface->Length);
status = SubmitUrb(functionPdoExt->parentFdoExt, selectIfaceUrb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)){
/*
* Replace the old PUSBD_INTERFACE_INFORMATION
* (which we got when we did select-configuration for the parent)
* with the new one.
*/
ASSERT(funcIface->Interface);
FREEPOOL(funcIface->Interface);
funcIface->Interface = MemDup(selectIface, selectIface->Length);
if (!funcIface->Interface){
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else {
ASSERT(NT_SUCCESS(status));
}
FREEPOOL(selectIfaceUrb);
}
else {
ASSERT(selectIfaceUrb);
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else {
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)){
/*
* Copy the interface information
*/
ASSERT(urbIface->Length == funcIface->Interface->Length);
RtlCopyMemory(urbIface, funcIface->Interface, funcIface->Interface->Length);
ASSERT(functionPdoExt->parentFdoExt->selectedConfigHandle);
urb->UrbSelectConfiguration.ConfigurationHandle = functionPdoExt->parentFdoExt->selectedConfigHandle;
}
}
else {
DBGERR(("invalid interface number"));
status = STATUS_INVALID_PARAMETER;
}
}
else {
DBGVERBOSE(("FunctionInternalDeviceControl - closing configuration"));
status = STATUS_SUCCESS;
}
return status;
}
/*
* UrbFunctionGetDescriptorFromDevice
*
*
* Note: this function cannot be pageable because internal
* ioctls may be sent at IRQL==DISPATCH_LEVEL.
*/
NTSTATUS UrbFunctionGetDescriptorFromDevice(PFUNCTION_PDO_EXT functionPdoExt, PURB urb)
{
NTSTATUS status;
switch (urb->UrbControlDescriptorRequest.DescriptorType){
case USB_DEVICE_DESCRIPTOR_TYPE:
DBGVERBOSE((" USB_DEVICE_DESCRIPTOR_TYPE"));
if (urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)){
RtlCopyMemory( urb->UrbControlDescriptorRequest.TransferBuffer,
&functionPdoExt->functionDeviceDesc,
sizeof(USB_DEVICE_DESCRIPTOR));
status = STATUS_SUCCESS;
}
else {
status = STATUS_INVALID_BUFFER_SIZE;
}
urb->UrbControlDescriptorRequest.TransferBufferLength = sizeof(USB_DEVICE_DESCRIPTOR);
break;
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
DBGVERBOSE((" USB_CONFIGURATION_DESCRIPTOR_TYPE"));
status = BuildFunctionConfigurationDescriptor(
functionPdoExt,
urb->UrbControlDescriptorRequest.TransferBuffer,
urb->UrbControlDescriptorRequest.TransferBufferLength,
&urb->UrbControlDescriptorRequest.TransferBufferLength);
break;
default:
/*
* Return NO_STATUS so that URB gets passed down to USBHUB.
*/
DBGVERBOSE(("UrbFunctionGetDescriptorFromDevice: Unhandled desc type: %xh.", (ULONG)urb->UrbControlDescriptorRequest.DescriptorType));
status = NO_STATUS;
break;
}
return status;
}