/* ************************************************************************* * File: URBFUNC.C * * Module: USBCCGP.SYS * USB Common Class Generic Parent driver. * * Copyright (c) 1998 Microsoft Corporation * * * Author: ervinp * ************************************************************************* */ #include #include #include #include #include #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; }