You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
750 lines
21 KiB
750 lines
21 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xsplit.c
|
|
|
|
Abstract:
|
|
|
|
splits a DMA transfer into multiple smallest transfers
|
|
that the miniport can handle.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
6-20-99 : created
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#endif
|
|
|
|
ULONG
|
|
USBPORT_MakeSplitTransfer(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PTRANSFER_SG_LIST SgList,
|
|
PHCD_TRANSFER_CONTEXT SplitTransfer,
|
|
ULONG MaxTransferLength,
|
|
ULONG MaxPacketLength,
|
|
PULONG Idx,
|
|
PULONG Offset,
|
|
ULONG BytesToMap,
|
|
ULONG LengthMapped
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PTRANSFER_SG_ENTRY32 sgEntry;
|
|
PTRANSFER_SG_LIST splitSgList;
|
|
ULONG length;
|
|
|
|
sgEntry = &SgList->SgEntry[*Idx];
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'splt',
|
|
SplitTransfer,
|
|
*Idx,
|
|
*Offset);
|
|
|
|
USBPORT_ASSERT(MaxTransferLength % MaxPacketLength == 0)
|
|
|
|
if ((sgEntry->Length - *Offset) > MaxTransferLength) {
|
|
|
|
// case 2, this transfer falls entirely within
|
|
// this sg entry
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt1',
|
|
MaxTransferLength,
|
|
*Offset,
|
|
sgEntry);
|
|
|
|
// make one transfer from sg[Idx]
|
|
// inc offset
|
|
|
|
length = MaxTransferLength;
|
|
|
|
splitSgList = &SplitTransfer->SgList;
|
|
splitSgList->SgCount = 1;
|
|
splitSgList->SgEntry[0].LogicalAddress.Hw32 =
|
|
sgEntry->LogicalAddress.Hw32 + *Offset;
|
|
splitSgList->SgEntry[0].SystemAddress =
|
|
sgEntry->SystemAddress + *Offset;
|
|
splitSgList->SgEntry[0].Length = length;
|
|
// start offset is always 0 for the first element
|
|
splitSgList->SgEntry[0].StartOffset = 0;
|
|
|
|
SplitTransfer->Tp.TransferBufferLength =
|
|
length;
|
|
|
|
SplitTransfer->Tp.MiniportFlags = MPTX_SPLIT_TRANSFER;
|
|
|
|
splitSgList->MdlVirtualAddress =
|
|
SgList->MdlVirtualAddress + LengthMapped;
|
|
|
|
splitSgList->MdlSystemAddress =
|
|
SgList->MdlSystemAddress + LengthMapped;
|
|
|
|
// indicate that this is a split child
|
|
SET_FLAG(SplitTransfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD);
|
|
|
|
*Offset += length;
|
|
BytesToMap -= length;
|
|
|
|
} else {
|
|
|
|
// make transfer from last part of sg[Idx]
|
|
// and first part of sg[Idx+1] if necessary,
|
|
// inc Idx
|
|
// reset offset
|
|
|
|
// case 2
|
|
length = sgEntry->Length - *Offset;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt2',
|
|
MaxTransferLength,
|
|
*Offset,
|
|
length);
|
|
|
|
USBPORT_ASSERT(length <= MaxTransferLength);
|
|
|
|
// last part of sg1;
|
|
splitSgList = &SplitTransfer->SgList;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sgE1',
|
|
splitSgList,
|
|
sgEntry,
|
|
0);
|
|
|
|
splitSgList->SgCount = 1;
|
|
splitSgList->SgEntry[0].LogicalAddress.Hw32 =
|
|
sgEntry->LogicalAddress.Hw32 + *Offset;
|
|
splitSgList->SgEntry[0].SystemAddress =
|
|
sgEntry->SystemAddress + *Offset;
|
|
splitSgList->SgEntry[0].Length = length;
|
|
// start offset is always 0 for the first element
|
|
splitSgList->SgEntry[0].StartOffset = 0;
|
|
|
|
SplitTransfer->Tp.TransferBufferLength =
|
|
length;
|
|
|
|
SplitTransfer->Tp.MiniportFlags = MPTX_SPLIT_TRANSFER;
|
|
|
|
splitSgList->MdlVirtualAddress =
|
|
SgList->MdlVirtualAddress + LengthMapped;
|
|
|
|
splitSgList->MdlSystemAddress =
|
|
SgList->MdlSystemAddress + LengthMapped;
|
|
|
|
// indicate that this is a split child
|
|
SET_FLAG(SplitTransfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD);
|
|
|
|
*Offset += length;
|
|
BytesToMap -= length;
|
|
|
|
// calculate max size of second part
|
|
length = MaxTransferLength - length;
|
|
|
|
if (length > BytesToMap) {
|
|
length = BytesToMap;
|
|
}
|
|
|
|
if (length == 0) {
|
|
|
|
(*Idx)++;
|
|
*Offset = 0;
|
|
|
|
} else {
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt3',
|
|
MaxTransferLength,
|
|
*Offset,
|
|
length);
|
|
|
|
(*Idx)++;
|
|
*Offset = 0;
|
|
|
|
sgEntry = &SgList->SgEntry[*Idx];
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sgE2',
|
|
splitSgList,
|
|
sgEntry,
|
|
*Idx);
|
|
|
|
splitSgList->SgCount++;
|
|
splitSgList->SgEntry[1].LogicalAddress.Hw32 =
|
|
sgEntry->LogicalAddress.Hw32 + *Offset;
|
|
splitSgList->SgEntry[1].SystemAddress =
|
|
sgEntry->SystemAddress + *Offset;
|
|
splitSgList->SgEntry[1].Length = length;
|
|
splitSgList->SgEntry[1].StartOffset =
|
|
splitSgList->SgEntry[0].Length;
|
|
|
|
SplitTransfer->Tp.TransferBufferLength += length;
|
|
|
|
*Offset += length;
|
|
BytesToMap -= length;
|
|
|
|
}
|
|
}
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt>',
|
|
BytesToMap,
|
|
0,
|
|
0);
|
|
|
|
return BytesToMap;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_SplitBulkInterruptTransfer(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PHCD_ENDPOINT Endpoint,
|
|
PHCD_TRANSFER_CONTEXT Transfer,
|
|
PLIST_ENTRY TransferList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Split a bulk or interrupt transfer
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
nt status code
|
|
|
|
--*/
|
|
{
|
|
ULONG maxTransferLength, maxPacketLength;
|
|
PHCD_TRANSFER_CONTEXT splitTransfer;
|
|
PTRANSFER_SG_LIST sgList;
|
|
LIST_ENTRY tmpList;
|
|
ULONG idx, i, offset, bytesToMap, lengthMapped;
|
|
ULONG numberOfSplits;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
sgList = &Transfer->SgList;
|
|
maxPacketLength = EP_MAX_PACKET(Endpoint);
|
|
USBPORT_ASSERT(EP_MAX_TRANSFER(Endpoint) >= maxPacketLength);
|
|
// round to the smallest multiple of max packet size
|
|
maxTransferLength =
|
|
(EP_MAX_TRANSFER(Endpoint)/maxPacketLength) * maxPacketLength;
|
|
|
|
// some notes:
|
|
|
|
//
|
|
//
|
|
// The MAXTRANSFER is equal to USB_PAGE_SIZE (4k) and
|
|
// the transfer sg list is broken into usb pages. In this
|
|
// case we construct a transfer structure for each pair of
|
|
// sg entries we round the length of the first sg entry down
|
|
// to the highest multiple of MAXPACKET so we get a split
|
|
// pattern like this:
|
|
//
|
|
// {Sg1}{.Sg2.}{.Sg3.}{.Sg4.} sg entries
|
|
// |------|------|------|------| page breaks
|
|
// |----------------------| original transfer
|
|
// <...><>
|
|
// <...><>
|
|
// <...><><.>
|
|
// {1 }{2 }{3 }{4} splits
|
|
//
|
|
|
|
// The MAXTRANSFER is less than USB_PAGE_SIZE (4k) and the
|
|
// transfer sg list is broken into usb pages.
|
|
//
|
|
// the pattern will look like this:
|
|
//
|
|
// {...Sg1......}{......Sg2......}{......Sg3......}
|
|
// |----------------|----------------|----------------|
|
|
// |-----------------------------------|
|
|
// <..>
|
|
// <..>
|
|
// <..>
|
|
// <><>
|
|
// <..>
|
|
// <..>
|
|
// <..>
|
|
// <><>
|
|
// <..>
|
|
// {1 }{2 }{3 }{4 }{5 }{6 }{7 }{8 }{9 }
|
|
|
|
// cases:
|
|
// case 1 - transfer lies within the sg entry
|
|
// case 2 - transfer overlaps the sg entry
|
|
|
|
|
|
//
|
|
// The MAXTRANSFER is greater than USB_PAGE_SIZE (4k)
|
|
// ie etc, we currently don't handle this case
|
|
//
|
|
// Note: since the buffer is currently completely mapped
|
|
// and locked it is better to tune the mniport to take the
|
|
// larger transfers if possible
|
|
|
|
if (EP_MAX_TRANSFER(Endpoint) > USB_PAGE_SIZE) {
|
|
|
|
BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
|
|
}
|
|
|
|
|
|
// allocate the split elements
|
|
|
|
// mark the parent transfer as a split
|
|
SET_FLAG(Transfer->Flags, USBPORT_TXFLAG_SPLIT);
|
|
|
|
numberOfSplits =
|
|
Transfer->Tp.TransferBufferLength / maxTransferLength + 1;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sptC',
|
|
numberOfSplits,
|
|
0,
|
|
0);
|
|
|
|
InitializeListHead(&tmpList);
|
|
|
|
for (i=0; i<numberOfSplits; i++) {
|
|
|
|
ALLOC_POOL_Z(splitTransfer,
|
|
NonPagedPool,
|
|
Transfer->TotalLength);
|
|
|
|
if (splitTransfer == NULL) {
|
|
goto SplitBulkInterruptTransfer_Fail;
|
|
}
|
|
|
|
RtlCopyMemory(splitTransfer,
|
|
Transfer,
|
|
Transfer->TotalLength);
|
|
|
|
splitTransfer->MiniportContext = (PUCHAR) splitTransfer;
|
|
splitTransfer->MiniportContext += splitTransfer->PrivateLength;
|
|
InitializeListHead(&splitTransfer->DoubleBufferList);
|
|
|
|
InsertTailList(&tmpList,
|
|
&splitTransfer->TransferLink);
|
|
|
|
}
|
|
|
|
idx = 0;
|
|
offset = 0;
|
|
bytesToMap = Transfer->Tp.TransferBufferLength;
|
|
lengthMapped = 0;
|
|
|
|
while (bytesToMap) {
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sptM',
|
|
bytesToMap,
|
|
offset,
|
|
idx);
|
|
|
|
listEntry = RemoveHeadList(&tmpList);
|
|
|
|
splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_CONTEXT,
|
|
TransferLink);
|
|
|
|
ASSERT_TRANSFER(splitTransfer);
|
|
|
|
bytesToMap = USBPORT_MakeSplitTransfer(FdoDeviceObject,
|
|
sgList,
|
|
splitTransfer,
|
|
maxTransferLength,
|
|
maxPacketLength,
|
|
&idx,
|
|
&offset,
|
|
bytesToMap,
|
|
lengthMapped);
|
|
|
|
lengthMapped += splitTransfer->Tp.TransferBufferLength;
|
|
|
|
InsertTailList(TransferList,
|
|
&splitTransfer->TransferLink);
|
|
|
|
InsertTailList(&Transfer->SplitTransferList,
|
|
&splitTransfer->SplitLink);
|
|
|
|
}
|
|
|
|
// free extra splits we did not use
|
|
while (!IsListEmpty(&tmpList)) {
|
|
|
|
listEntry = RemoveHeadList(&tmpList);
|
|
|
|
splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_CONTEXT,
|
|
TransferLink);
|
|
|
|
ASSERT_TRANSFER(splitTransfer);
|
|
|
|
UNSIG(splitTransfer);
|
|
FREE_POOL(FdoDeviceObject, splitTransfer);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
SplitBulkInterruptTransfer_Fail:
|
|
|
|
TEST_TRAP();
|
|
// free the tmp list
|
|
while (!IsListEmpty(&tmpList)) {
|
|
|
|
listEntry = RemoveHeadList(&tmpList);
|
|
|
|
splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_CONTEXT,
|
|
TransferLink);
|
|
|
|
ASSERT_TRANSFER(splitTransfer);
|
|
|
|
UNSIG(splitTransfer);
|
|
FREE_POOL(FdoDeviceObject, splitTransfer);
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
#if 0
|
|
NTSTATUS
|
|
USBPORT_SplitIsochronousTransfer(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PHCD_ENDPOINT Endpoint,
|
|
PHCD_TRANSFER_CONTEXT Transfer,
|
|
PLIST_ENTRY TransferList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Split an iso transfer
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PMINIPORT_ISO_TRANSFER isoTransfer, splitIsoTransfer;
|
|
PHCD_TRANSFER_CONTEXT splitTransfer;
|
|
LIST_ENTRY tmpList;
|
|
|
|
// first figure out how many child transfer structures
|
|
// we will need and allocate them, we do this based on
|
|
// the most packets we can fit in to a request
|
|
|
|
// we do not fixup the SG table for the child transfers
|
|
// since this information is not passed to the miniport
|
|
|
|
isoTransfer = Transfer->IsoTransfer;
|
|
|
|
transferCount = 0;
|
|
length = 0;
|
|
maxSplitLength = EP_MAX_TRANSFER(Endpoint);
|
|
|
|
for (i=0; i<isoTransfer->PacketCount; i++) {
|
|
if (length + isoTransfer->Packets[i].Length > maxSplitLength) {
|
|
length = 0;
|
|
transferCount++;
|
|
} else {
|
|
length += isoTransfer->Packets[i].Length
|
|
}
|
|
}
|
|
|
|
// transferCount is the number of child transfers,
|
|
// allocate them the now and clone the parent
|
|
InitializeListHead(tmpList);
|
|
for (i=0; i<transferCount; i++) {
|
|
TRANSFER_SG_LIST sgList;
|
|
|
|
splitTransfer = ALLOC()
|
|
if (splitTransfer == NULL) {
|
|
// release resources and return an error
|
|
TEST_TRAP();
|
|
xxx;
|
|
break;
|
|
}
|
|
RtlCopyMemory(splitTransfer,
|
|
Transfer,
|
|
Transfer->TotalLength);
|
|
|
|
sgList = &splitTransfer->SgList;
|
|
// zap the sg table since we don't use it for children
|
|
for (j=0; j<sgList->SgCount; j++) {
|
|
sgList->SgEntry[j].LogicalAddress = 0xffffffff;
|
|
sgList->SgEntry[j].SystemAddress = USB_BAD_PTR;
|
|
sgList->SgEntry[j].Length = 0xffffffff;
|
|
sgList->SgEntry[j].StartOffset = 0xffffffff;
|
|
}
|
|
sgList->Flags = 0xFFFFFFFF;
|
|
sgList->MdlVirtualAddress = USB_BAD_PTR;
|
|
sgList->MdlSystemAddress = USB_BAD_PTR;
|
|
sgList->SgCount = 0xFFFFFFFF;
|
|
|
|
InsertTailList(&tmpList,
|
|
&splitTransfer->TransferLink);
|
|
}
|
|
|
|
// we now have a list of child transfer structures.
|
|
|
|
// intialize them
|
|
pkt = 0;
|
|
systemAddress = isoTransfer->SystemAddress;
|
|
InitializeListHead(&Transfer->SplitTransferList);
|
|
do {
|
|
|
|
listEntry = RemoveHeadList(&tmpList);
|
|
|
|
splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_CONTEXT,
|
|
TransferLink);
|
|
|
|
ASSERT_TRANSFER(splitTransfer);
|
|
SET_FLAG(transfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD);
|
|
splitIsoTransfer = splitTransfer->IsoTransfer;
|
|
splitIsoTransfer->PacketCount = 0;
|
|
splitIsoTransfer->SystemAdderess = systemAddress;
|
|
splitLength = 0;
|
|
i = 0;
|
|
InsertTailList(TransferList,
|
|
&splitTransfer->TransferLink);
|
|
InsertTailList(Transfer->SplitTransferList,
|
|
&splitTransfer->SplitLink);
|
|
|
|
while (1) {
|
|
if (splitLength + isoTransfer->Packets[pkt].Length > maxSplitLength) {
|
|
// this transfer is filled, move to the next one
|
|
systemAddress += splitLength;
|
|
break;
|
|
} else {
|
|
splitIsoTransfer->Packets[i] = isoTransfer->Packets[pkt];
|
|
splitLength += splitIsoTransfer->Packets[i].Length;
|
|
splitIsoTransfer->PacketCount++;
|
|
pkt++;
|
|
i++;
|
|
}
|
|
}
|
|
} while (pkt < isoTransfer->PacketCount);
|
|
|
|
}
|
|
#endif
|
|
|
|
VOID
|
|
USBPORT_SplitTransfer(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PHCD_ENDPOINT Endpoint,
|
|
PHCD_TRANSFER_CONTEXT Transfer,
|
|
PLIST_ENTRY TransferList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Splits a transfer into multiple transfers of the proper size
|
|
for the miniport.
|
|
|
|
Returns a list of transfer structures that need to be added to
|
|
the active list. If the transfer does not need to be split the
|
|
list will contain only the original transfer.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
|
|
InitializeListHead(TransferList);
|
|
InitializeListHead(&Transfer->SplitTransferList);
|
|
Transfer->UsbdStatus = USBD_STATUS_SUCCESS;
|
|
|
|
if (Transfer->Tp.TransferBufferLength <= EP_MAX_TRANSFER(Endpoint)) {
|
|
// no split needed
|
|
InsertTailList(TransferList,
|
|
&Transfer->TransferLink);
|
|
return;
|
|
}
|
|
|
|
switch(Endpoint->Parameters.TransferType) {
|
|
case Interrupt:
|
|
case Bulk:
|
|
USBPORT_SplitBulkInterruptTransfer(FdoDeviceObject,
|
|
Endpoint,
|
|
Transfer,
|
|
TransferList);
|
|
break;
|
|
case Control:
|
|
// not supported yet
|
|
// although currently not supported the USBD stack never
|
|
// correctly implemented transfers > 4k so we fudge it here
|
|
//
|
|
BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
|
|
break;
|
|
case Isochronous:
|
|
BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_DoneSplitTransfer(
|
|
PHCD_TRANSFER_CONTEXT SplitTransfer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when a split transfer is completed by hardware
|
|
this function only completes active transfers
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_CONTEXT transfer;
|
|
PHCD_ENDPOINT endpoint;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
KIRQL tIrql;
|
|
|
|
endpoint = SplitTransfer->Endpoint;
|
|
ASSERT_ENDPOINT(endpoint);
|
|
fdoDeviceObject = endpoint->FdoDeviceObject;
|
|
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'dnSP',
|
|
SplitTransfer, 0, 0);
|
|
|
|
// get the parent
|
|
transfer = SplitTransfer->Transfer;
|
|
ASSERT_TRANSFER(transfer);
|
|
|
|
//
|
|
// copy the child data to the parent transfer
|
|
//
|
|
transfer->MiniportBytesTransferred +=
|
|
SplitTransfer->MiniportBytesTransferred;
|
|
|
|
// error ?
|
|
//
|
|
if (SplitTransfer->UsbdStatus != USBD_STATUS_SUCCESS &&
|
|
!TEST_FLAG(SplitTransfer->Flags, USBPORT_TXFLAG_KILL_SPLIT)) {
|
|
transfer->UsbdStatus = SplitTransfer->UsbdStatus;
|
|
}
|
|
|
|
ACQUIRE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
// remove this transfer from the list
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'rmSP',
|
|
transfer, 0, SplitTransfer);
|
|
RemoveEntryList(&SplitTransfer->SplitLink);
|
|
|
|
// flush any triple buffers
|
|
USBPORT_FlushAdapterDBs(fdoDeviceObject,
|
|
SplitTransfer);
|
|
|
|
// free this child
|
|
UNSIG(SplitTransfer);
|
|
FREE_POOL(fdoDeviceObject, SplitTransfer);
|
|
|
|
// is the transfer complete?
|
|
if (IsListEmpty(&transfer->SplitTransferList)) {
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'cpSP',
|
|
transfer, 0, 0);
|
|
RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
|
|
USBPORT_DoneTransfer(transfer);
|
|
} else {
|
|
RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_CancelSplitTransfer(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PHCD_TRANSFER_CONTEXT SplitTransfer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_CONTEXT transfer;
|
|
PHCD_ENDPOINT endpoint;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
KIRQL tIrql;
|
|
|
|
endpoint = SplitTransfer->Endpoint;
|
|
ASSERT_ENDPOINT(endpoint);
|
|
fdoDeviceObject = endpoint->FdoDeviceObject;
|
|
|
|
// remove the child, when all children are gone put the
|
|
// parent on the cancel list
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'caSP',
|
|
SplitTransfer, 0, 0);
|
|
|
|
// get the parent
|
|
transfer = SplitTransfer->Transfer;
|
|
ASSERT_TRANSFER(transfer);
|
|
|
|
//
|
|
// copy the child data to the parent transfer
|
|
//
|
|
transfer->MiniportBytesTransferred +=
|
|
SplitTransfer->MiniportBytesTransferred;
|
|
|
|
ACQUIRE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
// remove this transfer from the list
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'rmSP',
|
|
transfer, 0, SplitTransfer);
|
|
RemoveEntryList(&SplitTransfer->SplitLink);
|
|
RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
|
|
// free this child
|
|
UNSIG(SplitTransfer);
|
|
FREE_POOL(fdoDeviceObject, SplitTransfer);
|
|
|
|
// is the transfer complete?
|
|
if (IsListEmpty(&transfer->SplitTransferList)) {
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'cpSC',
|
|
transfer, 0, 0);
|
|
|
|
InsertTailList(&endpoint->CancelList, &transfer->TransferLink);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|