218 lines
7.9 KiB
C
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|