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.
1310 lines
40 KiB
1310 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
periodic.c
|
|
|
|
Abstract:
|
|
|
|
miniport transfer code for interrupt endpoints
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
1-1-00 : created, jdunn
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
/*
|
|
|
|
For USB 2 period indicates a microframe polling interval so
|
|
our tree structure is based on microframes.
|
|
|
|
|
|
|- 1 ms frame -->|<--------- microframe ------------->|
|
|
mic <32> <16> <08> <04> <02> <01>
|
|
|
|
(table entry)
|
|
|
|
[FRAME.MICROFRAME]
|
|
[0.0] 0 ( 0) -\
|
|
( 0)-\
|
|
[2.0] 16 ( 1) -/ \
|
|
( 0)-\
|
|
[1.0] 8 ( 2) -\ / \
|
|
( 1)-/ \
|
|
[3.0] 24 ( 3) -/ \
|
|
(0)-\
|
|
[0.4] 4 ( 4) -\ / \
|
|
( 2)-\ / \
|
|
[2.4] 20 ( 5) -/ \ / \
|
|
( 1)-/ \
|
|
[1.4] 12 ( 6) -\ / \
|
|
( 3)-/ \
|
|
[3.4] 28 ( 7) -/ \
|
|
(0)-\
|
|
[0.2] 2 ( 8) -\ / \
|
|
( 4)-\ / \
|
|
[2.2] 18 ( 9) -/ \ / \
|
|
( 2)-\ / \
|
|
[1.2] 10 (10) -\ / \ / \
|
|
( 5)-/ \ / \
|
|
[3.2] 26 (11) -/ \ / \
|
|
(1)-/ \
|
|
[0.6] 6 (12) -\ / \
|
|
( 6)-\ / \
|
|
[2.6] 22 (13) -/ \ / \
|
|
( 3)-/ \
|
|
[1.6] 14 (14) -\ / \
|
|
( 7)-/ \
|
|
[3.6] 30 (15) -/ \
|
|
(0)
|
|
[0.1] 1 (16) -\ /
|
|
( 8)-\ /
|
|
[2.1] 17 (17) -/ \ /
|
|
( 4)-\ /
|
|
[1.1] 9 (18) -\ / \ /
|
|
( 9)-/ \ /
|
|
[3.1] 25 (19) -/ \ /
|
|
(2)-\ /
|
|
[0.5] 5 (20) -\ / \ /
|
|
(10)-\ / \ /
|
|
[2.5] 21 (21) -/ \ / \ /
|
|
( 5)-/ \ /
|
|
[1.5] 13 (22) -\ / \ /
|
|
(11)-/ \ /
|
|
[3.5] 29 (23) -/ \ /
|
|
(1)-/
|
|
[0.3] 3 (24) -\ /
|
|
(12)-\ /
|
|
[2.3] 19 (25) -/ \ /
|
|
( 6)-\ /
|
|
[1.3] 11 (26) -\ / \ /
|
|
(13)-/ \ /
|
|
[3.3] 27 (27) -/ \ /
|
|
(3)-/
|
|
[0.7] 7 (28) -\ /
|
|
(14)-\ /
|
|
[2.7] 23 (29) -/ \ /
|
|
( 7)-/
|
|
[1.7] 15 (30) -\ /
|
|
(15)-/
|
|
[3.7] 31 (31) -/
|
|
|
|
|
|
Allocations:
|
|
period.offset table entries
|
|
1 0, 1, 2.........31
|
|
|
|
2.0 0, 1, 2.........15
|
|
2.1 16,17,18.........31
|
|
|
|
4.0 0, 1, 2..........7
|
|
4.1 8, 9,10.........15
|
|
4.2 16,17,18.........23
|
|
4.3 24,25,26.........31
|
|
|
|
8.0 0, 1, 2, 3
|
|
8.1 4, 5, 6, 7
|
|
8.2 8, 9,10,11
|
|
8.3 12,13,14,15
|
|
8.4 16,17,18,19
|
|
8.5 20,21,22,23
|
|
8.6 24,25,26,27
|
|
8.7 28,29,30,31
|
|
|
|
...
|
|
|
|
|
|
we mainatin a set of dummy queue heads that correspond to the 1ms nodes
|
|
in the chart above.
|
|
|
|
the queue head table has 4 entries QH 0..3
|
|
|
|
frame mic frame qh
|
|
0 0..7 <0>
|
|
1 0..7 <1>
|
|
|
|
driver maintains a mini tree that has seven QHs that are placed in the
|
|
schedule.
|
|
|
|
period frame(microframes)
|
|
|
|
32(4) 16(2) 1(8)
|
|
|
|
frame
|
|
|
|
0 (a 0) -\
|
|
(e 0)-\
|
|
2 (b 1) -/ \
|
|
(g 0)-
|
|
1 (c 2) -\ /
|
|
(f 1)-/
|
|
3 (d 3) -/
|
|
|
|
|
|
|
|
idx QH frame
|
|
0 a 0
|
|
1 b 2
|
|
2 c 1
|
|
3 d 3
|
|
4 e 0,2
|
|
5 f 1,3
|
|
6 g 0,2,1,3
|
|
*/
|
|
|
|
/*
|
|
|
|
We represent each possible node in the tree with a data structure that decodes
|
|
the appropriate queue head and S-Mask for the node
|
|
|
|
*e.g
|
|
for period 8 microframe, sched offset 0 QH = g s-mask = 1
|
|
|
|
// The structure contains entries for the 64 possible nodes
|
|
// plus the static ED for bulk and control (2) each entry
|
|
// corresponds to the period in the following table.
|
|
//
|
|
// the array looks like this:
|
|
// 1, 2, 2, 4, 4, 4, 4, 8,
|
|
// 8, 8, 8, 8, 8, 8, 8,16,
|
|
// 16,16,16,16,16,16,16,16,
|
|
// 16,16,16,16,16,16,16,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,
|
|
|
|
queue heads used for high speed
|
|
|
|
0 (3) -\
|
|
(1)-\
|
|
2 (4) -/ \
|
|
(0)-
|
|
1 (5) -\ /
|
|
(2)-/
|
|
3 (6) -/
|
|
|
|
*/
|
|
|
|
/* offsets for each period list */
|
|
|
|
#define ED_INTERRUPT_1mf 0 //period = 1mf
|
|
#define ED_INTERRUPT_2mf 1 //period = 2mf
|
|
#define ED_INTERRUPT_4mf 3 //period = 4mf
|
|
#define ED_INTERRUPT_8mf 7 //period = 8mf
|
|
#define ED_INTERRUPT_16mf 15 //period = 16mf
|
|
#define ED_INTERRUPT_32mf 31 //period = 32mf
|
|
|
|
#define ED_INTERRUPT_1ms 0 //period = 1ms
|
|
#define ED_INTERRUPT_2ms 1 //period = 2ms
|
|
#define ED_INTERRUPT_4ms 3 //period = 4ms
|
|
#define ED_INTERRUPT_8ms 7 //period = 8ms
|
|
#define ED_INTERRUPT_16ms 15 //period = 16ms
|
|
#define ED_INTERRUPT_32ms 31 //period = 32ms
|
|
|
|
|
|
PERIOD_TABLE periodTable[64] =
|
|
{ // period, qh-idx, s-mask
|
|
1, 0, 0xFF, // 1111 1111 bits 0..7
|
|
|
|
2, 0, 0x55, // 0101 0101 bits 0,2,4,6
|
|
2, 0, 0xAA, // 1010 1010 bits 1,3,5,7
|
|
|
|
4, 0, 0x11, // 0001 0001 bits 0,4
|
|
4, 0, 0x44, // 0100 0100 bits 2,6
|
|
4, 0, 0x22, // 0010 0010 bits 1,5
|
|
4, 0, 0x88, // 1000 1000 bits 3,7
|
|
|
|
8, 0, 0x01, // 0000 0001 bits 0
|
|
8, 0, 0x10, // 0001 0000 bits 4
|
|
8, 0, 0x04, // 0000 0100 bits 2
|
|
8, 0, 0x40, // 0100 0000 bits 6
|
|
8, 0, 0x02, // 0000 0010 bits 1
|
|
8, 0, 0x20, // 0010 0000 bits 5
|
|
8, 0, 0x08, // 0000 1000 bits 3
|
|
8, 0, 0x80, // 1000 0000 bits 7
|
|
|
|
16, 1, 0x01, // 0000 0001 bits 0
|
|
16, 2, 0x01, // 0000 0001 bits 0
|
|
16, 1, 0x10, // 0001 0000 bits 4
|
|
16, 2, 0x10, // 0001 0000 bits 4
|
|
16, 1, 0x04, // 0000 0100 bits 2
|
|
16, 2, 0x04, // 0000 0100 bits 2
|
|
16, 1, 0x40, // 0100 0000 bits 6
|
|
16, 2, 0x40, // 0100 0000 bits 6
|
|
16, 1, 0x02, // 0000 0010 bits 1
|
|
16, 2, 0x02, // 0000 0010 bits 1
|
|
16, 1, 0x20, // 0010 0000 bits 5
|
|
16, 2, 0x20, // 0010 0000 bits 5
|
|
16, 1, 0x08, // 0000 1000 bits 3
|
|
16, 2, 0x08, // 0000 1000 bits 3
|
|
16, 1, 0x80, // 1000 0000 bits 7
|
|
16, 2, 0x80, // 1000 0000 bits 7
|
|
|
|
32, 3, 0x01, // 0000 0000 bits 0
|
|
32, 5, 0x01, // 0000 0000 bits 0
|
|
32, 4, 0x01, // 0000 0000 bits 0
|
|
32, 6, 0x01, // 0000 0000 bits 0
|
|
32, 3, 0x10, // 0000 0000 bits 4
|
|
32, 5, 0x10, // 0000 0000 bits 4
|
|
32, 4, 0x10, // 0000 0000 bits 4
|
|
32, 6, 0x10, // 0000 0000 bits 4
|
|
32, 3, 0x04, // 0000 0000 bits 2
|
|
32, 5, 0x04, // 0000 0000 bits 2
|
|
32, 4, 0x04, // 0000 0000 bits 2
|
|
32, 6, 0x04, // 0000 0000 bits 2
|
|
32, 3, 0x40, // 0000 0000 bits 6
|
|
32, 5, 0x40, // 0000 0000 bits 6
|
|
32, 4, 0x40, // 0000 0000 bits 6
|
|
32, 6, 0x40, // 0000 0000 bits 6
|
|
32, 3, 0x02, // 0000 0000 bits 1
|
|
32, 5, 0x02, // 0000 0000 bits 1
|
|
32, 4, 0x02, // 0000 0000 bits 1
|
|
32, 6, 0x02, // 0000 0000 bits 1
|
|
32, 3, 0x20, // 0000 0000 bits 5
|
|
32, 5, 0x20, // 0000 0000 bits 5
|
|
32, 4, 0x20, // 0000 0000 bits 5
|
|
32, 6, 0x20, // 0000 0000 bits 5
|
|
32, 3, 0x04, // 0000 0000 bits 3
|
|
32, 5, 0x04, // 0000 0000 bits 3
|
|
32, 4, 0x04, // 0000 0000 bits 3
|
|
32, 6, 0x04, // 0000 0000 bits 3
|
|
32, 3, 0x40, // 0000 0000 bits 7
|
|
32, 5, 0x40, // 0000 0000 bits 7
|
|
32, 4, 0x40, // 0000 0000 bits 7
|
|
32, 6, 0x40, // 0000 0000 bits 7
|
|
|
|
};
|
|
|
|
VOID
|
|
EHCI_EnablePeriodicList(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
USBCMD cmd;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
|
|
cmd.PeriodicScheduleEnable = 1;
|
|
|
|
WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul,
|
|
cmd.ul);
|
|
|
|
LOGENTRY(DeviceData, G, '_enP', cmd.ul, 0, 0);
|
|
|
|
}
|
|
|
|
UCHAR ClassicPeriodIdx[8] = {
|
|
ED_INTERRUPT_1ms, //period = 1ms
|
|
ED_INTERRUPT_2ms, //period = 2ms
|
|
ED_INTERRUPT_4ms, //period = 4ms
|
|
ED_INTERRUPT_8ms, //period = 8ms
|
|
ED_INTERRUPT_16ms,//period = 16ms
|
|
ED_INTERRUPT_32ms,//period = 32ms
|
|
ED_INTERRUPT_32ms,//period = 64ms
|
|
ED_INTERRUPT_32ms //period = 128ms
|
|
};
|
|
|
|
USB_MINIPORT_STATUS
|
|
EHCI_OpenInterruptEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
OUT PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PUCHAR buffer;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys, qhPhys;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
ULONG i;
|
|
ULONG tdCount, bytes, offset;
|
|
PPERIOD_TABLE periodTableEntry;
|
|
BOOLEAN classic;
|
|
PHCD_TRANSFER_DESCRIPTOR dummyTd;
|
|
UCHAR periodIdx[8] = {
|
|
ED_INTERRUPT_1mf, //period = 1mf
|
|
ED_INTERRUPT_2mf, //period = 2mf
|
|
ED_INTERRUPT_4mf, //period = 4mf
|
|
ED_INTERRUPT_8mf, //period = 8mf
|
|
ED_INTERRUPT_16mf,//period = 16mf
|
|
ED_INTERRUPT_32mf,//period = 32mf
|
|
ED_INTERRUPT_32mf,//period = 64mf
|
|
ED_INTERRUPT_32mf //period = 128mf
|
|
};
|
|
|
|
classic =
|
|
(EndpointData->Parameters.DeviceSpeed != HighSpeed) ? TRUE : FALSE;
|
|
|
|
LOGENTRY(DeviceData, G, '_opI', EndpointData, EndpointParameters, classic);
|
|
|
|
// select the proper list
|
|
// the period is a power of 2 ie
|
|
// 32,16,8,4,2,1
|
|
// we just need to find which bit is set
|
|
GET_BIT_SET(EndpointParameters->Period, i);
|
|
EHCI_ASSERT(DeviceData, i < 8);
|
|
EHCI_ASSERT(DeviceData, EndpointParameters->Period < 64);
|
|
|
|
InitializeListHead(&EndpointData->DoneTdList);
|
|
|
|
buffer = EndpointParameters->CommonBufferVa;
|
|
phys = EndpointParameters->CommonBufferPhys;
|
|
offset = EndpointParameters->ScheduleOffset;
|
|
|
|
if (classic) {
|
|
i = ClassicPeriodIdx[i];
|
|
periodTableEntry = NULL;
|
|
} else {
|
|
i = periodIdx[i];
|
|
periodTableEntry = &periodTable[i+offset];
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_iep', EndpointData,
|
|
periodTableEntry, i);
|
|
|
|
// locate the appropriate queue head and period
|
|
// table entry
|
|
|
|
if (classic) {
|
|
EndpointData->StaticQH =
|
|
DeviceData->StaticInterruptQH[i+offset];
|
|
EndpointData->PeriodTableEntry = NULL;
|
|
} else {
|
|
EndpointData->StaticQH =
|
|
DeviceData->StaticInterruptQH[periodTableEntry->qhIdx];
|
|
EndpointData->PeriodTableEntry = periodTableEntry;
|
|
}
|
|
|
|
// how much did we get
|
|
bytes = EndpointParameters->CommonBufferBytes;
|
|
|
|
EndpointData->QhChkPhys = phys;
|
|
EndpointData->QhChk = buffer;
|
|
RtlZeroMemory(buffer, 256);
|
|
phys += 256;
|
|
buffer += 256;
|
|
bytes -= 256;
|
|
|
|
// make the Ed
|
|
qh = (PHCD_QUEUEHEAD_DESCRIPTOR) buffer;
|
|
qhPhys = phys;
|
|
|
|
phys += sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
|
|
buffer += sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
|
|
bytes -= sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
|
|
|
|
tdCount = bytes/sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
EHCI_ASSERT(DeviceData, tdCount >= TDS_PER_INTERRUPT_ENDPOINT);
|
|
|
|
EndpointData->TdList = (PHCD_TD_LIST) buffer;
|
|
EndpointData->TdCount = tdCount;
|
|
for (i=0; i<tdCount; i++) {
|
|
EHCI_InitializeTD(DeviceData,
|
|
EndpointData,
|
|
&EndpointData->TdList->Td[i],
|
|
phys);
|
|
|
|
phys += sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
}
|
|
|
|
EndpointData->FreeTds = tdCount;
|
|
|
|
EndpointData->QueueHead =
|
|
EHCI_InitializeQH(DeviceData,
|
|
EndpointData,
|
|
qh,
|
|
qhPhys);
|
|
|
|
if (classic) {
|
|
// use mask parameters passed to us
|
|
qh->HwQH.EpCaps.InterruptScheduleMask =
|
|
EndpointParameters->InterruptScheduleMask;
|
|
qh->HwQH.EpCaps.SplitCompletionMask =
|
|
EndpointParameters->SplitCompletionMask;
|
|
|
|
} else {
|
|
qh->HwQH.EpCaps.InterruptScheduleMask =
|
|
periodTableEntry->InterruptScheduleMask;
|
|
}
|
|
|
|
// init our polling variables
|
|
dummyTd = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
dummyTd->HwTD.Next_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
TRANSFER_DESCRIPTOR_PTR(dummyTd->NextHcdTD) = NULL;
|
|
dummyTd->HwTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
TRANSFER_DESCRIPTOR_PTR(dummyTd->AltNextHcdTD) = NULL;
|
|
dummyTd->HwTD.Token.Active = 0;
|
|
SET_FLAG(dummyTd->Flags, TD_FLAG_DUMMY);
|
|
EndpointData->DummyTd = dummyTd;
|
|
EndpointData->HcdHeadP = dummyTd;
|
|
|
|
// endpoint is not active, set up the overlay
|
|
// so that the currentTD is the Dummy
|
|
|
|
qh->HwQH.CurrentTD.HwAddress = dummyTd->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_InsertQueueHeadInPeriodicList(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert an interrupt endpoint into the h/w schedule
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR staticQH, qh, nxtQH, prvQH;
|
|
HW_LINK_POINTER hLink;
|
|
|
|
staticQH = EndpointData->StaticQH;
|
|
qh = EndpointData->QueueHead;
|
|
|
|
EHCI_ASSERT(DeviceData,
|
|
TEST_FLAG(staticQH->QhFlags, EHCI_QH_FLAG_STATIC));
|
|
|
|
EHCI_ASSERT(DeviceData,
|
|
!TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE));
|
|
|
|
nxtQH = QH_DESCRIPTOR_PTR(staticQH->NextQh);
|
|
prvQH = staticQH;
|
|
|
|
// Note: This function must be coherent with the budgeter code
|
|
// the budgeter inserts endpoints such that the newer endpoints
|
|
// are at the end of the sublist, older are at the begining. The
|
|
// lower the ordinal value the older the endpoint. The ordinal
|
|
// values are used to maintain the same ordering in the event the
|
|
// schedule must be reconstructed
|
|
|
|
// hook this queue head to the the static
|
|
// queue head list, two cases
|
|
|
|
// case 1:
|
|
// insert QH1, queue head list is not empty:
|
|
//
|
|
// |staticQH|<->QH2<->QH3<->|staticQH|<->QH4
|
|
//
|
|
// |staticQH|<->QH2<->QH3<->QH1<->|staticQH|<->QH4
|
|
// (o=1) (o=2) (o=3)
|
|
// for case one qeue must insert the queue head in the list
|
|
// based on the ordinal value we need to compute prev and
|
|
// next
|
|
|
|
|
|
// case 2:
|
|
// insert QH1, queue head sublist is empty
|
|
//
|
|
// |staticQH|<->|staticQH|<->QH4
|
|
//
|
|
// |staticQH|<->QH1<->|staticQH|<->QH4
|
|
|
|
LOGENTRY(DeviceData, G, '_inQ', EndpointData, qh, staticQH);
|
|
|
|
// find the correct spot
|
|
// prvQH, nxtQH are currently the beginnig and end of the
|
|
// sublist
|
|
qh->Ordinal = EndpointData->Parameters.Ordinal;
|
|
qh->Period = EndpointData->Parameters.Period;
|
|
|
|
if (TEST_FLAG(prvQH->QhFlags, EHCI_QH_FLAG_STATIC) &&
|
|
(nxtQH == NULL || TEST_FLAG(nxtQH->QhFlags, EHCI_QH_FLAG_STATIC))) {
|
|
// case 2 qh list is empty
|
|
|
|
LOGENTRY(DeviceData, G, '_iq1', prvQH, 0, nxtQH);
|
|
|
|
} else {
|
|
// case 1 qh list is not empty
|
|
|
|
// find the correct position based on ordinal
|
|
while (nxtQH != NULL &&
|
|
!TEST_FLAG(nxtQH->QhFlags, EHCI_QH_FLAG_STATIC) &&
|
|
qh->Ordinal > nxtQH->Ordinal) {
|
|
|
|
prvQH = nxtQH;
|
|
nxtQH = QH_DESCRIPTOR_PTR(prvQH->NextQh);
|
|
|
|
}
|
|
|
|
//if (nxtQH != NULL &&
|
|
// !TEST_FLAG(nxtQH->QhFlags, EHCI_QH_FLAG_STATIC)) {
|
|
// // middle insertion
|
|
// TEST_TRAP();
|
|
//}
|
|
}
|
|
|
|
|
|
// do the insertion
|
|
|
|
QH_DESCRIPTOR_PTR(qh->NextQh) = nxtQH;
|
|
QH_DESCRIPTOR_PTR(qh->PrevQh) = prvQH;
|
|
// next link points back
|
|
if (nxtQH != NULL &&
|
|
!TEST_FLAG(nxtQH->QhFlags, EHCI_QH_FLAG_STATIC)) {
|
|
QH_DESCRIPTOR_PTR(nxtQH->PrevQh) = qh;
|
|
}
|
|
|
|
// prev points to new qh
|
|
QH_DESCRIPTOR_PTR(prvQH->NextQh) = qh;
|
|
|
|
// now link to HW,order of operation is
|
|
// important here
|
|
hLink.HwAddress = qh->PhysicalAddress;
|
|
SET_QH(hLink.HwAddress);
|
|
|
|
qh->HwQH.HLink = prvQH->HwQH.HLink;
|
|
prvQH->HwQH.HLink = hLink;
|
|
|
|
SET_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_RemoveQueueHeadFromPeriodicList(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
remove an interrupt endpoint into from the h/w schedule
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR staticQH, qh, prevQH, nextQH;
|
|
HW_LINK_POINTER hLink;
|
|
|
|
staticQH = EndpointData->StaticQH;
|
|
qh = EndpointData->QueueHead;
|
|
|
|
if (!TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE)) {
|
|
return;
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_rmQ', EndpointData, qh, staticQH);
|
|
|
|
|
|
|
|
// remove the queue head
|
|
|
|
// remove QH2, two cases
|
|
// |staticQH|<->QH1<->QH2<->QH3<->|staticQH|<->QH4
|
|
//
|
|
// |staticQH|<->QH1<->QH3<->|staticQH|<->QH4
|
|
|
|
|
|
prevQH = QH_DESCRIPTOR_PTR(qh->PrevQh);
|
|
nextQH = QH_DESCRIPTOR_PTR(qh->NextQh);
|
|
|
|
// unlink next ptrs
|
|
QH_DESCRIPTOR_PTR(prevQH->NextQh) = nextQH;
|
|
if (nextQH != NULL &&
|
|
!TEST_FLAG(nextQH->QhFlags, EHCI_QH_FLAG_STATIC)) {
|
|
QH_DESCRIPTOR_PTR(nextQH->PrevQh) = prevQH;
|
|
}
|
|
|
|
// hw unlink, nextqh will be null if this is period 1ms
|
|
// qh
|
|
if (nextQH == NULL) {
|
|
hLink.HwAddress = 0;
|
|
SET_T_BIT(hLink.HwAddress);
|
|
} else {
|
|
hLink.HwAddress = nextQH->PhysicalAddress;
|
|
SET_QH(hLink.HwAddress);
|
|
}
|
|
prevQH->HwQH.HLink = hLink;
|
|
|
|
CLEAR_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE);
|
|
QH_DESCRIPTOR_PTR(qh->NextQh) = NULL;
|
|
QH_DESCRIPTOR_PTR(qh->PrevQh) = NULL;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
EHCI_InterruptTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_PARAMETERS TransferUrb,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize interrupt Transfer
|
|
|
|
NOTES:
|
|
|
|
HW pointers nextTD and AltNextTD are shadowed in
|
|
NextHcdTD and AltNextHcdTD.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
PHCD_TRANSFER_DESCRIPTOR firstTd, prevTd, td;
|
|
ULONG lengthMapped;
|
|
|
|
// if we have enough TDs do the transfer
|
|
if (EndpointData->FreeTds == 0) {
|
|
TEST_TRAP();
|
|
LOGENTRY(DeviceData, G, '_IIS', EndpointData, TransferUrb, 0);
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
EndpointData->PendingTransfers++;
|
|
|
|
// if we have enough tds, program the transfer
|
|
|
|
LOGENTRY(DeviceData, G, '_IIT', EndpointData, TransferUrb, 0);
|
|
|
|
lengthMapped = 0;
|
|
prevTd = NULL;
|
|
|
|
while (lengthMapped < TransferUrb->TransferBufferLength) {
|
|
|
|
TransferContext->PendingTds++;
|
|
td = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
if (TransferContext->PendingTds == 1) {
|
|
firstTd = td;
|
|
} else if (prevTd) {
|
|
SET_NEXT_TD(DeviceData, prevTd, td);
|
|
}
|
|
|
|
//
|
|
// fields for data TD
|
|
//
|
|
|
|
// use direction specified in transfer
|
|
if (TEST_FLAG(TransferUrb->TransferFlags, USBD_TRANSFER_DIRECTION_IN)) {
|
|
td->HwTD.Token.Pid = HcTOK_In;
|
|
} else {
|
|
td->HwTD.Token.Pid = HcTOK_Out;
|
|
}
|
|
|
|
td->HwTD.Token.DataToggle = HcTOK_Toggle1;
|
|
td->HwTD.Token.Active = 1;
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_dta', td, lengthMapped, TransferUrb->TransferBufferLength);
|
|
|
|
lengthMapped =
|
|
EHCI_MapAsyncTransferToTd(DeviceData,
|
|
EndpointData->Parameters.MaxPacketSize,
|
|
lengthMapped,
|
|
NULL,
|
|
TransferContext,
|
|
td,
|
|
TransferSGList);
|
|
|
|
prevTd = td;
|
|
|
|
}
|
|
|
|
// interrupt on the last TD
|
|
td->HwTD.Token.InterruptOnComplete = 1;
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds, td->PhysicalAddress, firstTd);
|
|
|
|
// td points to last TD in this transfer, point it at the dummy
|
|
SET_NEXT_TD(DeviceData, td, EndpointData->DummyTd);
|
|
|
|
EHCI_LinkTransferToQueue(DeviceData,
|
|
EndpointData,
|
|
firstTd);
|
|
|
|
ASSERT_DUMMY_TD(DeviceData, EndpointData->DummyTd);
|
|
|
|
// tell the hc we have periodic transfers available
|
|
EHCI_EnablePeriodicList(DeviceData);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
CLASSIC
|
|
|
|
The classic tree has 63 possible nodes, usport bw manager will select the
|
|
appropriate node based on a 'classic' bus.
|
|
|
|
usbport maintains bandwidth management for each classic bus, however
|
|
budgeting the microframes is left to the miniport.
|
|
|
|
classic 1ms interrupt schedule, NOTE:this schedule shares some queue heads with
|
|
the hish speed schedule.
|
|
|
|
* = shared queue head
|
|
|
|
fr <32> <16> <08> <04> <02> <01>
|
|
|
|
|
|
0 ( 0) -\
|
|
( 0)-\
|
|
16 ( 1) -/ \
|
|
( 0)-\
|
|
8 ( 2) -\ / \
|
|
( 1)-/ \
|
|
24 ( 3) -/ \
|
|
*(0)-\
|
|
4 ( 4) -\ / \
|
|
( 2)-\ / \
|
|
20 ( 5) -/ \ / \
|
|
( 1)-/ \
|
|
12 ( 6) -\ / \
|
|
( 3)-/ \
|
|
28 ( 7) -/ \
|
|
*(0)-\
|
|
2 ( 8) -\ / \
|
|
( 4)-\ / \
|
|
18 ( 9) -/ \ / \
|
|
( 2)-\ / \
|
|
10 (10) -\ / \ / \
|
|
( 5)-/ \ / \
|
|
26 (11) -/ \ / \
|
|
*(1)-/ \
|
|
6 (12) -\ / \
|
|
( 6)-\ / \
|
|
22 (13) -/ \ / \
|
|
( 3)-/ \
|
|
14 (14) -\ / \
|
|
( 7)-/ \
|
|
30 (15) -/ \
|
|
*(0)
|
|
1 (16) -\ /
|
|
( 8)-\ /
|
|
17 (17) -/ \ /
|
|
( 4)-\ /
|
|
9 (18) -\ / \ /
|
|
( 9)-/ \ /
|
|
25 (19) -/ \ /
|
|
*(2)-\ /
|
|
5 (20) -\ / \ /
|
|
(10)-\ / \ /
|
|
21 (21) -/ \ / \ /
|
|
( 5)-/ \ /
|
|
13 (22) -\ / \ /
|
|
(11)-/ \ /
|
|
29 (23) -/ \ /
|
|
*(1)-/
|
|
3 (24) -\ /
|
|
(12)-\ /
|
|
19 (25) -/ \ /
|
|
( 6)-\ /
|
|
11 (26) -\ / \ /
|
|
(13)-/ \ /
|
|
27 (27) -/ \ /
|
|
*(3)-/
|
|
7 (28) -\ /
|
|
(14)-\ /
|
|
23 (29) -/ \ /
|
|
( 7)-/
|
|
15 (30) -\ /
|
|
(15)-/
|
|
31 (31) -/
|
|
|
|
|
|
The node table is arrangened in the standard usb 1.1 fashion so that
|
|
the schedule offset passed to us by the budget engine applies
|
|
|
|
// the static array looks like this:
|
|
// 1, 2, 2, 4, 4, 4, 4, 8,
|
|
// 8, 8, 8, 8, 8, 8, 8,16,
|
|
// 16,16,16,16,16,16,16,16,
|
|
// 16,16,16,16,16,16,16,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,
|
|
|
|
|
|
CLASSIC BUDGET
|
|
|
|
The classic budget is maintained by the port driver we
|
|
simply need to program the endpoint at the appropriate
|
|
offset (node) with the given smask cmask
|
|
|
|
period(ms) queue head(index)
|
|
1 0
|
|
2 1
|
|
2 2
|
|
4 3
|
|
4 4
|
|
4 5
|
|
4 6
|
|
8 7
|
|
8 8
|
|
8 9
|
|
8 10
|
|
8 11
|
|
8 12
|
|
8 13
|
|
8 14
|
|
|
|
*/
|
|
|
|
UCHAR EHCI_Frame2Qhead[32] = {
|
|
/*
|
|
offset ms frame
|
|
*/
|
|
0, // 0
|
|
16,// 1
|
|
8, // 2
|
|
24,// 3
|
|
4, // 4
|
|
20,// 5
|
|
12,// 6
|
|
28,// 7
|
|
2, // 8
|
|
18,// 9
|
|
10,// 10
|
|
26,// 11
|
|
6, // 12
|
|
22,// 13
|
|
14,// 14
|
|
30,// 15
|
|
1, // 16
|
|
17,// 17
|
|
9, // 18
|
|
25,// 19
|
|
5, // 20
|
|
21,// 21
|
|
13,// 22
|
|
29,// 23
|
|
3, // 24
|
|
19,// 25
|
|
11,// 26
|
|
27,// 27
|
|
7, // 28
|
|
23,// 29
|
|
15,// 30
|
|
31,// 31
|
|
};
|
|
|
|
PHCD_QUEUEHEAD_DESCRIPTOR
|
|
EHCI_GetQueueHeadForFrame(
|
|
PDEVICE_DATA DeviceData,
|
|
ULONG Frame
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
static queue head associated with a particular frame
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
ULONG f;
|
|
|
|
// normalize frame
|
|
f = Frame%32;
|
|
|
|
qh = DeviceData->StaticInterruptQH[EHCI_Frame2Qhead[f]+ED_INTERRUPT_32ms];
|
|
|
|
return qh;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
Queue head index table, values correspond to index in StaticQueueHead List
|
|
|
|
fr <32> <16> <08> <04> <02> <01>
|
|
|
|
|
|
0 (31) -\
|
|
(15)-\
|
|
16 (32) -/ \
|
|
(7)-\
|
|
8 (33) -\ / \
|
|
(16)-/ \
|
|
24 (34) -/ \
|
|
*(3)-\
|
|
4 (35) -\ / \
|
|
(17)-\ / \
|
|
20 (36) -/ \ / \
|
|
(8)-/ \
|
|
12 (37) -\ / \
|
|
(18)-/ \
|
|
28 (38) -/ \
|
|
*(1)-\
|
|
2 (39) -\ / \
|
|
(19)-\ / \
|
|
18 (40) -/ \ / \
|
|
(9)-\ / \
|
|
10 (41) -\ / \ / \
|
|
(20)-/ \ / \
|
|
26 (42) -/ \ / \
|
|
*(4)-/ \
|
|
6 (43) -\ / \
|
|
(21)-\ / \
|
|
22 (44) -/ \ / \
|
|
(10)-/ \
|
|
14 (45) -\ / \
|
|
(22)-/ \
|
|
30 (46) -/ \
|
|
*(0)
|
|
1 (47) -\ /
|
|
(23)-\ /
|
|
17 (48) -/ \ /
|
|
(11)-\ /
|
|
9 (49) -\ / \ /
|
|
(24)-/ \ /
|
|
25 (50) -/ \ /
|
|
*(5)-\ /
|
|
5 (51) -\ / \ /
|
|
(25)-\ / \ /
|
|
21 (51) -/ \ / \ /
|
|
(12)-/ \ /
|
|
13 (53) -\ / \ /
|
|
(26)-/ \ /
|
|
29 (54) -/ \ /
|
|
*(2)-/
|
|
3 (55) -\ /
|
|
(27)-\ /
|
|
19 (56) -/ \ /
|
|
(13)-\ /
|
|
11 (57) -\ / \ /
|
|
(28)-/ \ /
|
|
27 (58) -/ \ /
|
|
*(6)-/
|
|
7 (59) -\ /
|
|
(29)-\ /
|
|
23 (60) -/ \ /
|
|
(14)-/
|
|
15 (61) -\ /
|
|
(30)-/
|
|
31 (62) -/
|
|
|
|
*/
|
|
|
|
UCHAR EHCI_QHeadLinkTable[63] = {
|
|
/* nextQueueHead QueueHead */
|
|
0xff, // 0
|
|
0, // 1
|
|
0, // 2
|
|
1, // 3
|
|
1, // 4
|
|
2, // 5
|
|
2, // 6
|
|
3, // 7
|
|
3, // 8
|
|
4, // 9
|
|
4, // 10
|
|
5, // 11
|
|
5, // 12
|
|
6, // 13
|
|
6, // 14
|
|
7, // 15
|
|
7, // 16
|
|
8, // 17
|
|
8, // 18
|
|
9, // 19
|
|
9, // 20
|
|
10, // 21
|
|
10, // 22
|
|
11, // 23
|
|
11, // 24
|
|
12, // 25
|
|
12, // 26
|
|
13, // 27
|
|
13, // 28
|
|
14, // 29
|
|
14, // 30
|
|
15, // 31
|
|
15, // 32
|
|
16, // 33
|
|
16, // 34
|
|
17, // 35
|
|
17, // 36
|
|
18, // 37
|
|
18, // 38
|
|
19, // 39
|
|
19, // 40
|
|
20, // 41
|
|
20, // 42
|
|
21, // 43
|
|
21, // 44
|
|
22, // 45
|
|
22, // 46
|
|
23, // 47
|
|
23, // 48
|
|
24, // 49
|
|
24, // 50
|
|
25, // 51
|
|
25, // 52
|
|
26, // 53
|
|
26, // 54
|
|
27, // 55
|
|
27, // 56
|
|
28, // 57
|
|
28, // 58
|
|
29, // 59
|
|
29, // 60
|
|
30, // 61
|
|
30, // 62
|
|
};
|
|
|
|
|
|
VOID
|
|
EHCI_InitailizeInterruptSchedule(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
ULONG i;
|
|
|
|
// first initialize all the 'dummy' queue heads
|
|
|
|
for (i=0; i<63; i++) {
|
|
qh = DeviceData->StaticInterruptQH[i];
|
|
|
|
SET_T_BIT(qh->HwQH.Overlay.qTD.Next_qTD.HwAddress);
|
|
qh->HwQH.Overlay.qTD.Token.Halted = 1;
|
|
qh->HwQH.EpChars.HeadOfReclimationList = 0;
|
|
qh->Sig = SIG_HCD_IQH;
|
|
}
|
|
|
|
|
|
#define INIT_QH(q, nq, f) \
|
|
do {\
|
|
QH_DESCRIPTOR_PTR((q)->NextQh) = (nq); \
|
|
QH_DESCRIPTOR_PTR((q)->PrevQh) = NULL; \
|
|
(q)->HwQH.HLink.HwAddress = (nq)->PhysicalAddress; \
|
|
(q)->HwQH.HLink.HwAddress |= EHCI_DTYPE_QH;\
|
|
(q)->HwQH.EpCaps.InterruptScheduleMask =0xff;\
|
|
(q)->QhFlags |= EHCI_QH_FLAG_STATIC;\
|
|
(q)->QhFlags |= f;\
|
|
} while(0)
|
|
|
|
// now build the above tree
|
|
for (i=1; i<63; i++) {
|
|
INIT_QH(DeviceData->StaticInterruptQH[i],
|
|
DeviceData->StaticInterruptQH[EHCI_QHeadLinkTable[i]],
|
|
i<=6 ? EHCI_QH_FLAG_HIGHSPEED : 0);
|
|
}
|
|
|
|
// last qh has t bit set
|
|
|
|
DeviceData->StaticInterruptQH[0]->HwQH.HLink.HwAddress = 0;
|
|
SET_T_BIT(DeviceData->StaticInterruptQH[0]->HwQH.HLink.HwAddress);
|
|
DeviceData->StaticInterruptQH[0]->QhFlags |=
|
|
(EHCI_QH_FLAG_HIGHSPEED | EHCI_QH_FLAG_STATIC);
|
|
|
|
#undef INIT_QH
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_WaitFrames(
|
|
PDEVICE_DATA DeviceData,
|
|
ULONG Frames
|
|
)
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hcOp = NULL;
|
|
FRINDEX frameIndex;
|
|
ULONG frameNumber, i, c;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
for (c=0; c< Frames; c++) {
|
|
// bugbug this code does not handle varaible frame list
|
|
// sizes
|
|
frameIndex.ul = READ_REGISTER_ULONG(&hcOp->UsbFrameIndex.ul);
|
|
|
|
frameNumber = (ULONG) frameIndex.FrameListCurrentIndex;
|
|
// shift off the microframes
|
|
frameNumber >>= 3;
|
|
|
|
i = frameNumber;
|
|
|
|
do {
|
|
frameIndex.ul = READ_REGISTER_ULONG(&hcOp->UsbFrameIndex.ul);
|
|
|
|
frameNumber = (ULONG) frameIndex.FrameListCurrentIndex;
|
|
// shift off the microframes
|
|
frameNumber >>= 3;
|
|
} while (frameNumber == i);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_RebalanceInterruptEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
compute how much common buffer we will need
|
|
for this endpoint
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
// update internal copy of parameters
|
|
EndpointData->Parameters = *EndpointParameters;
|
|
|
|
// period promotion?
|
|
if (qh->Period != EndpointParameters->Period) {
|
|
ULONG i, offset;
|
|
|
|
EHCI_KdPrint((DeviceData, 1, "'period change old - %d new %d\n",
|
|
qh->Period, EndpointParameters->Period));
|
|
|
|
EHCI_RemoveQueueHeadFromPeriodicList(DeviceData,
|
|
EndpointData);
|
|
|
|
EHCI_WaitFrames(DeviceData, 2);
|
|
|
|
// clear residual data from overlay area
|
|
qh->HwQH.Overlay.qTD.Token.ErrorCounter = 0;
|
|
qh->HwQH.Overlay.qTD.Token.SplitXstate = 0;
|
|
qh->HwQH.Overlay.Ov.OverlayDw8.CprogMask = 0;
|
|
qh->HwQH.Overlay.Ov.OverlayDw9.Sbytes = 0;
|
|
qh->HwQH.Overlay.Ov.OverlayDw9.fTag = 0;
|
|
|
|
|
|
EHCI_ASSERT(DeviceData,
|
|
EndpointData->Parameters.DeviceSpeed != HighSpeed);
|
|
|
|
// select the proper list
|
|
// the period is a power of 2 ie
|
|
// 32,16,8,4,2,1
|
|
// we just need to find which bit is set
|
|
GET_BIT_SET(EndpointParameters->Period, i);
|
|
EHCI_ASSERT(DeviceData, i < 8);
|
|
EHCI_ASSERT(DeviceData, EndpointParameters->Period < 64);
|
|
|
|
offset = EndpointParameters->ScheduleOffset;
|
|
|
|
i = ClassicPeriodIdx[i];
|
|
EndpointData->StaticQH =
|
|
DeviceData->StaticInterruptQH[i+offset];
|
|
EndpointData->PeriodTableEntry = NULL;
|
|
|
|
qh->Period = EndpointParameters->Period;
|
|
qh->HwQH.EpCaps.InterruptScheduleMask =
|
|
EndpointParameters->InterruptScheduleMask;
|
|
qh->HwQH.EpCaps.SplitCompletionMask =
|
|
EndpointParameters->SplitCompletionMask;
|
|
|
|
EHCI_InsertQueueHeadInPeriodicList(DeviceData,
|
|
EndpointData);
|
|
|
|
} else {
|
|
|
|
EHCI_RemoveQueueHeadFromPeriodicList(DeviceData,
|
|
EndpointData);
|
|
|
|
EHCI_WaitFrames(DeviceData, 2);
|
|
|
|
// clear residual data from overlay area
|
|
qh->HwQH.Overlay.qTD.Token.ErrorCounter = 0;
|
|
qh->HwQH.Overlay.qTD.Token.SplitXstate = 0;
|
|
qh->HwQH.Overlay.Ov.OverlayDw8.CprogMask = 0;
|
|
qh->HwQH.Overlay.Ov.OverlayDw9.Sbytes = 0;
|
|
qh->HwQH.Overlay.Ov.OverlayDw9.fTag = 0;
|
|
|
|
qh->HwQH.EpCaps.InterruptScheduleMask =
|
|
EndpointParameters->InterruptScheduleMask;
|
|
qh->HwQH.EpCaps.SplitCompletionMask =
|
|
EndpointParameters->SplitCompletionMask;
|
|
|
|
EHCI_InsertQueueHeadInPeriodicList(DeviceData,
|
|
EndpointData);
|
|
}
|
|
|
|
}
|
|
|