Source code of Windows XP (NT5)
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.

752 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. usb2lib.c
  5. Abstract:
  6. interface to usb2lib, usb2 low/full speed scheduling algorithms
  7. Environment:
  8. kernel or user mode only
  9. Notes:
  10. Revision History:
  11. 10-31-00 : created
  12. --*/
  13. #include "common.h"
  14. USB2LIB_DATA LibData;
  15. VOID
  16. USB2LIB_InitializeLib(
  17. PULONG HcContextSize,
  18. PULONG EndpointContextSize,
  19. PULONG TtContextSize,
  20. PUSB2LIB_DBGPRINT Usb2LibDbgPrint,
  21. PUSB2LIB_DBGBREAK Usb2LibDbgBreak
  22. )
  23. /*++
  24. Routine Description:
  25. Arguments:
  26. Return Value:
  27. --*/
  28. {
  29. *HcContextSize = sizeof(USB2LIB_HC_CONTEXT);
  30. *TtContextSize = sizeof(USB2LIB_TT_CONTEXT);
  31. *EndpointContextSize = sizeof(USB2LIB_ENDPOINT_CONTEXT);
  32. LibData.DbgPrint = Usb2LibDbgPrint;
  33. LibData.DbgBreak = Usb2LibDbgBreak;
  34. }
  35. VOID
  36. USB2LIB_InitController(
  37. PUSB2LIB_HC_CONTEXT HcContext
  38. )
  39. /*++
  40. Routine Description:
  41. Called at init time for an instance of the USB 2
  42. controller
  43. Arguments:
  44. Return Value:
  45. --*/
  46. {
  47. DBGPRINT(("USB2LIB_InitController %x\n", HcContext));
  48. HcContext->Sig = SIG_LIB_HC;
  49. init_hc(&HcContext->Hc);
  50. init_tt(&HcContext->Hc, &HcContext->DummyTt); // set up dummy TT for use by HS endpoints
  51. }
  52. VOID
  53. USB2LIB_InitTt(
  54. PUSB2LIB_HC_CONTEXT HcContext,
  55. PUSB2LIB_TT_CONTEXT TtContext
  56. )
  57. /*++
  58. Routine Description:
  59. Arguments:
  60. Return Value:
  61. --*/
  62. {
  63. DBGPRINT(("USB2LIB_InitTt %x %x\n", HcContext, TtContext));
  64. TtContext->Sig = SIG_LIB_TT;
  65. init_tt(&HcContext->Hc, &TtContext->Tt);
  66. }
  67. #if 1
  68. void Shift_to_list_end(
  69. int move_ep,
  70. PEndpoint RebalanceList[]
  71. )
  72. {
  73. // int i;
  74. PEndpoint ep = RebalanceList[move_ep];
  75. move_ep++;
  76. while (RebalanceList[move_ep])
  77. {
  78. RebalanceList[move_ep-1] = RebalanceList[move_ep];
  79. move_ep++;
  80. }
  81. RebalanceList[move_ep-1] = ep;
  82. }
  83. #endif
  84. BOOLEAN
  85. Promote_endpoint_periods(
  86. PEndpoint ep,
  87. PEndpoint RebalanceList[],
  88. PULONG RebalanceListEntries
  89. )
  90. {
  91. int unwind = 0, check_ep;
  92. unsigned result;
  93. if ((ep->actual_period != 1) && (ep->ep_type == interrupt) && (ep->start_microframe > 2))
  94. {
  95. DBGPRINT((">Period Promotion of allocated endpoint\n"));
  96. // To promote an endpoint period:
  97. // 0) unwind = false
  98. // 1) deallocate original endpoint
  99. // 2) change new ep period to 1
  100. // 3) (re)allocate new endpoint (with new period 1)
  101. // 4) if successful
  102. // 5) check endpoints in change list for need of period promotion
  103. // 6) deallocate endpoint, move to end of change list, change period to 1, reallocate
  104. // 7) if unsuccessful
  105. // 8) unwind = true; break
  106. // 9) next ep
  107. //10) if unwind
  108. //11) deallocate orginal ep
  109. //12) check change list for promotion endpoint(s)
  110. //13) if promoted ep
  111. //14) deallocate ep, change back to original period, allocate
  112. //15) next ep
  113. //16) return false
  114. //17) else return true
  115. //18) else return false
  116. /*
  117. // On return, change list will have promoted endpoints in order of reallocation, but it is possible
  118. // to have other endpoints interspersed with the promoted endpoints. The corresponding schedule of endpoints
  119. // must be adjusted to match the order of the promoted endpoints (since they are reinserted into the budget).
  120. // The promoted endpoints (except the original endpoint) are moved to the end of the change list as the
  121. // promotion reallocations are done to ensure that they are in the change list in the order of insertion
  122. // into the budget. This allows the scheduler to derive the new schedule/budget order from the order the
  123. // promoted endpoints appear in the change list.
  124. //
  125. // This algorithm (critically) depends on the Allocate/Deallocate "appending"/reusing an existing change list
  126. // as the "final" change list is composed during the period promotion processing is performed.
  127. */
  128. Deallocate_time_for_endpoint(ep,
  129. RebalanceList,
  130. RebalanceListEntries);
  131. ep->saved_period = ep->period;
  132. ep->period = 1;
  133. // 3) (re)allocate new endpoint (with new period 1)
  134. result = Allocate_time_for_endpoint(ep,
  135. RebalanceList,
  136. RebalanceListEntries);
  137. if (!result) {
  138. ep->period = ep->saved_period;
  139. ep->saved_period = 0;
  140. ep->promoted_this_time = 0;
  141. return 0; // failed period promotion of original endpoint
  142. }
  143. }
  144. check_ep = 0;
  145. while (RebalanceList[check_ep])
  146. {
  147. RebalanceList[check_ep]->promoted_this_time = 0;
  148. check_ep++;
  149. }
  150. check_ep = 0;
  151. while (RebalanceList[check_ep])
  152. {
  153. if ((RebalanceList[check_ep]->actual_period != 1) &&
  154. (RebalanceList[check_ep]->ep_type == interrupt) &&
  155. (RebalanceList[check_ep]->start_microframe > 2))
  156. {
  157. // 6) deallocate endpoint, move to end of change list, change period to 1, reallocate
  158. DBGPRINT((">Period Promoting endpoint\n"));
  159. Deallocate_time_for_endpoint(
  160. RebalanceList[check_ep],
  161. RebalanceList,
  162. RebalanceListEntries);
  163. // Shift_to_list_end(check_ep, RebalanceList);
  164. RebalanceList[check_ep]->promoted_this_time = 1;
  165. RebalanceList[check_ep]->saved_period = RebalanceList[check_ep]->period;
  166. RebalanceList[check_ep]->period = 1;
  167. result = Allocate_time_for_endpoint(
  168. RebalanceList[check_ep],
  169. RebalanceList,
  170. RebalanceListEntries);
  171. if (!result)
  172. {
  173. unwind = 1;
  174. break;
  175. }
  176. }
  177. check_ep++;
  178. }
  179. if (unwind)
  180. {
  181. DBGPRINT((">Unwinding Promoted endpoints\n"));
  182. //11) deallocate orginal ep
  183. Deallocate_time_for_endpoint(
  184. ep,
  185. RebalanceList,
  186. RebalanceListEntries);
  187. ep->period = ep->saved_period;
  188. ep->saved_period = 0;
  189. //12) check change list for promotion endpoint(s)
  190. check_ep = 0;
  191. while (RebalanceList[check_ep])
  192. {
  193. //13) if promoted ep
  194. if (RebalanceList[check_ep]->promoted_this_time)
  195. {
  196. //14) deallocate ep, change back to original period, allocate
  197. DBGPRINT((">Reallocating Unpromoted endpoint\n"));
  198. if(RebalanceList[check_ep]->calc_bus_time != 0)
  199. Deallocate_time_for_endpoint(
  200. RebalanceList[check_ep],
  201. RebalanceList,
  202. RebalanceListEntries);
  203. RebalanceList[check_ep]->period = RebalanceList[check_ep]->saved_period;
  204. RebalanceList[check_ep]->saved_period = 0;
  205. // Leave the promoted flag set since order could have changed.
  206. // schedule must be reconciled accordingly by the HC code.
  207. //RebalanceList[check_ep]->promoted_this_time = 0;
  208. result = Allocate_time_for_endpoint(
  209. RebalanceList[check_ep],
  210. RebalanceList,
  211. RebalanceListEntries);
  212. }
  213. check_ep++;
  214. }
  215. return 0;
  216. } else {
  217. return 1;
  218. }
  219. }
  220. BOOLEAN
  221. USB2LIB_AllocUsb2BusTime(
  222. PUSB2LIB_HC_CONTEXT HcContext,
  223. PUSB2LIB_TT_CONTEXT TtContext,
  224. PUSB2LIB_ENDPOINT_CONTEXT EndpointContext,
  225. PUSB2LIB_BUDGET_PARAMETERS Budget,
  226. PVOID RebalanceContext,
  227. PVOID RebalanceList,
  228. PULONG RebalanceListEntries
  229. )
  230. /*++
  231. Routine Description:
  232. Arguments:
  233. Return Value:
  234. --*/
  235. {
  236. eptype endpointType;
  237. unsigned direction, speed;
  238. //PEndpoint changed_ep_list[];
  239. unsigned result;
  240. //unsigned changed_eps;
  241. PEndpoint ep;
  242. BOOLEAN alloced;
  243. ULONG ilop;
  244. PREBALANCE_LIST rbl;
  245. PTT tt;
  246. ep = &EndpointContext->Ep;
  247. EndpointContext->Sig = SIG_LIB_EP;
  248. EndpointContext->RebalanceContext = RebalanceContext;
  249. //changed_ep_list = RebalanceList;
  250. switch (Budget->TransferType) {
  251. case Budget_Iso:
  252. DBGPRINT((">Iso \n"));
  253. endpointType = isoch;
  254. break;
  255. case Budget_Interrupt:
  256. DBGPRINT((">Interrupt \n"));
  257. endpointType = interrupt;
  258. break;
  259. default:
  260. TEST_TRAP();
  261. }
  262. if (Budget->Direction == Budget_In) {
  263. DBGPRINT((">In \n"));
  264. direction = INDIR;
  265. } else {
  266. DBGPRINT((">Out \n"));
  267. direction = OUTDIR;
  268. }
  269. switch (Budget->Speed) {
  270. case Budget_FullSpeed:
  271. DBGPRINT((">FullSpeed \n"));
  272. speed = FSSPEED;
  273. tt = &TtContext->Tt;
  274. break;
  275. case Budget_HighSpeed:
  276. DBGPRINT((">HighSpeed \n"));
  277. speed = HSSPEED;
  278. tt = &HcContext->DummyTt; // set endpoint to dummy TT so HC can be reached
  279. break;
  280. case Budget_LowSpeed:
  281. DBGPRINT((">LowSpeed \n"));
  282. speed = LSSPEED;
  283. tt = &TtContext->Tt;
  284. break;
  285. default:
  286. DBGPRINT(("BAD SPEED\n"));
  287. }
  288. DBGPRINT((">Period %d\n", Budget->Period));
  289. if(Budget->Speed == Budget_HighSpeed) {
  290. // This value should be a power of 2, so we don't have to check
  291. // but limit its value to MAXFRAMES * 8
  292. if(Budget->Period > MAXMICROFRAMES) {
  293. Budget->Period = MAXMICROFRAMES;
  294. }
  295. } else {
  296. // We are full / low speed endpoint
  297. //
  298. // Round down the period to the nearest power of two (if it isn't already)
  299. //
  300. for(ilop = MAXFRAMES; ilop >= 1; ilop = ilop >> 1) {
  301. if(Budget->Period >= ilop) {
  302. break;
  303. }
  304. }
  305. Budget->Period = ilop;
  306. }
  307. DBGPRINT((">MaxPacket %d\n", Budget->MaxPacket));
  308. DBGPRINT((">Converted Period %d\n", Budget->Period));
  309. DBGPRINT((">RebalanceListEntries %d\n", *RebalanceListEntries));
  310. Set_endpoint(
  311. ep,
  312. endpointType,
  313. direction,
  314. speed,
  315. Budget->Period,
  316. Budget->MaxPacket,
  317. tt);
  318. // ask John Garney to do the math
  319. DBGPRINT((">alloc (ep) %x \n", ep));
  320. result = Allocate_time_for_endpoint(ep,
  321. RebalanceList,
  322. RebalanceListEntries);
  323. // check if successful, period != 1, interrupt, and "late" in frame,
  324. // then need to promote period to 1
  325. // DBGPRINT((">Executing Promote_endpoint_periods (ep) %x \n", ep));
  326. if (result)
  327. {
  328. result = Promote_endpoint_periods(ep,
  329. RebalanceList,
  330. RebalanceListEntries);
  331. }
  332. // nonzero indicates success
  333. if (result) {
  334. // set return parameters
  335. DBGPRINT((">Results\n"));
  336. DBGPRINT((">num_starts %d \n", ep->num_starts));
  337. DBGPRINT((">num_completes %d \n", ep->num_completes));
  338. DBGPRINT((">start_microframe %d \n", ep->start_microframe));
  339. // this is the schedule offset
  340. DBGPRINT((">start_frame %d \n", ep->start_frame));
  341. // period awarded, may be less than requested
  342. DBGPRINT((">actual_period %d \n", ep->actual_period));
  343. DBGPRINT((">start_time %d \n", ep->start_time));
  344. DBGPRINT((">calc_bus_time %d \n", ep->calc_bus_time));
  345. DBGPRINT((">promoted_this_time %d \n", ep->promoted_this_time));
  346. alloced = TRUE;
  347. } else {
  348. alloced = FALSE;
  349. }
  350. // fix up rebalance list
  351. rbl = RebalanceList;
  352. ilop = 0;
  353. while (rbl->RebalanceContext[ilop]) {
  354. PUSB2LIB_ENDPOINT_CONTEXT endpointContext;
  355. DBGPRINT((">rb[%d] %x\n", ilop, rbl->RebalanceContext[ilop]));
  356. endpointContext = CONTAINING_RECORD(rbl->RebalanceContext[ilop],
  357. struct _USB2LIB_ENDPOINT_CONTEXT,
  358. Ep);
  359. rbl->RebalanceContext[ilop] = endpointContext->RebalanceContext;
  360. ilop++;
  361. }
  362. DBGPRINT((">Change List Size = %d RBE = %d\n", ilop, *RebalanceListEntries));
  363. *RebalanceListEntries = ilop;
  364. return alloced;
  365. }
  366. VOID
  367. USB2LIB_FreeUsb2BusTime(
  368. PUSB2LIB_HC_CONTEXT HcContext,
  369. PUSB2LIB_TT_CONTEXT TtContext,
  370. PUSB2LIB_ENDPOINT_CONTEXT EndpointContext,
  371. PVOID RebalanceList,
  372. PULONG RebalanceListEntries
  373. )
  374. /*++
  375. Routine Description:
  376. Arguments:
  377. Return Value:
  378. --*/
  379. {
  380. unsigned result;
  381. PEndpoint ep;
  382. PREBALANCE_LIST rbl;
  383. ULONG i;
  384. // ASSERT(EndpointContext->Sig == SIG_LIB_EP);
  385. ep = &EndpointContext->Ep;
  386. DBGPRINT((">dealloc ep Context = 0x%x (ep) %x \n", EndpointContext, ep));
  387. DBGPRINT((">RebalanceListEntries %d \n", *RebalanceListEntries));
  388. Deallocate_time_for_endpoint(ep,
  389. RebalanceList,
  390. RebalanceListEntries);
  391. // fix up rebalance list
  392. rbl = RebalanceList;
  393. i = 0;
  394. while (rbl->RebalanceContext[i]) {
  395. PUSB2LIB_ENDPOINT_CONTEXT endpointContext;
  396. DBGPRINT((">rb[%d] %x\n", i, rbl->RebalanceContext[i]));
  397. endpointContext = CONTAINING_RECORD(rbl->RebalanceContext[i],
  398. struct _USB2LIB_ENDPOINT_CONTEXT,
  399. Ep);
  400. rbl->RebalanceContext[i] = endpointContext->RebalanceContext;
  401. i++;
  402. }
  403. DBGPRINT((">Change List Size = %d RBE = %d\n", i, *RebalanceListEntries));
  404. *RebalanceListEntries = i;
  405. }
  406. VOID
  407. ConvertBtoHFrame(UCHAR BFrame, UCHAR BUFrame, PUCHAR HFrame, PUCHAR HUFrame)
  408. {
  409. // The budgeter returns funky values that we have to convert to something
  410. // that the host controller understands.
  411. // If bus micro frame is -1, that means that the start split is scheduled
  412. // in the last microframe of the previous bus frame.
  413. // to convert to hframes, you simply change the microframe to 0 and
  414. // keep the bus frame (see one of the tables in the host controller spec
  415. // eg 4-17.
  416. if(BUFrame == 0xFF) {
  417. *HUFrame = 0;
  418. *HFrame = BFrame;
  419. }
  420. // if the budgeter returns a value in the range from 0-6
  421. // we simply add one to the bus micro frame to get the host
  422. // microframe
  423. if(BUFrame >= 0 && BUFrame <= 6) {
  424. *HUFrame = BUFrame + 1;
  425. *HFrame = BFrame;
  426. }
  427. // if the budgeter returns a value of 7 for the bframe
  428. // then the HUframe = 0 and the HUframe = buframe +1
  429. if(BUFrame == 7) {
  430. *HUFrame = 0;
  431. *HFrame = BFrame + 1;
  432. }
  433. }
  434. UCHAR
  435. USB2LIB_GetSMASK(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  436. {
  437. PEndpoint Ep;
  438. UCHAR tmp = 0;
  439. Ep = &EndpointContext->Ep;
  440. // ASSERT(EndpointContext->Sig == SIG_LIB_EP);
  441. if(Ep->speed == HSSPEED) {
  442. //DBGPRINT(("in GetSMASK StartUFrame on High Speed Endpoint = 0x%x\n", Ep->start_microframe));
  443. tmp |= 1 << Ep->start_microframe;
  444. } else {
  445. ULONG ilop;
  446. UCHAR HFrame; // H (Host) frame for endpoint
  447. UCHAR HUFrame; // H (Host) micro frame for endpoint
  448. // For Full and Low Speed Endpoints
  449. // the budgeter returns a bframe. Convert to HUFrame to get SMASK
  450. ConvertBtoHFrame((UCHAR)Ep->start_frame, (UCHAR)Ep->start_microframe, &HFrame, &HUFrame);
  451. for(ilop = 0; ilop < Ep->num_starts; ilop++) {
  452. tmp |= 1 << HUFrame++;
  453. }
  454. }
  455. return tmp;
  456. }
  457. //
  458. // I'm too brain dead to calculate this so just do table lookup
  459. //
  460. // Calculated by 1 << Start H Frame + 2. If Start H Frame + 2 > 7 wrap the bits
  461. // to the lower part of the word
  462. // eg. hframe 0 +2 means cmask in frames 2,3,4 ==> cmask 0x1c
  463. // eg. hframe 5 + 2 means cmasks in frames 7, 8, 9 which means cmask 0x83
  464. #define SIZE_OF_CMASK 8
  465. static UCHAR CMASKS [SIZE_OF_CMASK] =
  466. { 0x1c, // Start HUFRAME 0
  467. 0x38, // Start HUFRAME 1
  468. 0x70, // Start HUFRAME 2
  469. 0xE0, // Start HUFRAME 3
  470. 0xC1, // Start HUFRAME 4
  471. 0x83, // Start HUFRAME 5
  472. 0x07, // Start HUFRAME 6
  473. 0x0E, // Start HUFRAME 7
  474. };
  475. UCHAR
  476. USB2LIB_GetCMASK(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  477. {
  478. PEndpoint Ep;
  479. Ep = &EndpointContext->Ep;
  480. // ASSERT(EndpointContext->Sig == SIG_LIB_EP);
  481. if(Ep->speed == HSSPEED) {
  482. return 0;
  483. } else if(Ep->ep_type == interrupt) {
  484. UCHAR HFrame; // H (Host) frame for endpoint
  485. UCHAR HUFrame; // H (Host) micro frame for endpoint
  486. ConvertBtoHFrame((UCHAR)Ep->start_frame, (UCHAR)Ep->start_microframe,
  487. &HFrame, &HUFrame);
  488. return CMASKS[HUFrame];
  489. } else {
  490. // Split ISO!
  491. UCHAR HFrame; // H (Host) frame for endpoint
  492. UCHAR HUFrame; // H (Host) micro frame for endpoint
  493. UCHAR tmp;
  494. ULONG NumCompletes;
  495. if(Ep->direction == OUTDIR) {
  496. // Split iso out -- NO complete splits
  497. return 0;
  498. }
  499. ConvertBtoHFrame((UCHAR)Ep->start_frame, (UCHAR)Ep->start_microframe,
  500. &HFrame, &HUFrame);
  501. HUFrame += 2;
  502. NumCompletes = Ep->num_completes;
  503. // ASSERT(NumCompletes > 0);
  504. //
  505. // Set all CMASKS bits to be set at the end of the frame
  506. //
  507. for(; HUFrame < 8; HUFrame++) {
  508. tmp |= 1 << HUFrame;
  509. NumCompletes--;
  510. if(!NumCompletes){
  511. break;
  512. }
  513. }
  514. //
  515. // Now set all CMASKS bits to be set at the end of the
  516. // frame I.E. for the next frame wrap condition
  517. //
  518. while(NumCompletes) {
  519. tmp |= 1 << (HUFrame - 8);
  520. NumCompletes--;
  521. }
  522. //DBGPRINT(("in GetCMASK HFRAME = 0x%x HUFRAME 0x%x\n", HFrame, HUFrame));
  523. return tmp;
  524. }
  525. }
  526. UCHAR
  527. USB2LIB_GetStartMicroFrame(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  528. {
  529. PEndpoint Ep;
  530. UCHAR HFrame; // H (Host) frame for endpoint
  531. UCHAR HUFrame; // H (Host) micro frame for endpoint
  532. Ep = &EndpointContext->Ep;
  533. // ASSERT(EndpointContext->Sig == SIG_LIB_EP);
  534. ConvertBtoHFrame((UCHAR)Ep->start_frame, (UCHAR)Ep->start_microframe,
  535. &HFrame, &HUFrame);
  536. return HUFrame;
  537. }
  538. UCHAR
  539. USB2LIB_GetPromotedThisTime(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  540. {
  541. PEndpoint Ep;
  542. UCHAR Promoted = 0;
  543. Ep = &EndpointContext->Ep;
  544. // ASSERT(EndpointContext->Sig == SIG_LIB_EP);
  545. Promoted = (UCHAR) Ep->promoted_this_time;
  546. Ep->promoted_this_time = 0;
  547. return Promoted;
  548. }
  549. UCHAR
  550. USB2LIB_GetNewPeriod(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  551. {
  552. PEndpoint Ep;
  553. Ep = &EndpointContext->Ep;
  554. // ASSERT(EndpointContext->Sig == SIG_LIB_EP);
  555. return (UCHAR) Ep->actual_period;
  556. }
  557. ULONG
  558. USB2LIB_GetScheduleOffset(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  559. {
  560. PEndpoint Ep;
  561. Ep = &EndpointContext->Ep;
  562. // assert(EndpointContext->Sig == SIG_LIB_EP);
  563. return Ep->start_frame;
  564. }
  565. PVOID
  566. USB2LIB_GetEndpoint(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  567. {
  568. return &(EndpointContext->Ep);
  569. }
  570. ULONG
  571. USB2LIB_GetAllocedBusTime(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  572. {
  573. PEndpoint Ep;
  574. Ep = &EndpointContext->Ep;
  575. // assert(EndpointContext->Sig == SIG_LIB_EP);
  576. return Ep->calc_bus_time;
  577. }
  578. PVOID
  579. USB2LIB_GetNextEndpoint(PUSB2LIB_ENDPOINT_CONTEXT EndpointContext)
  580. {
  581. PEndpoint Ep, nextEp;
  582. PUSB2LIB_ENDPOINT_CONTEXT nextContext;
  583. Ep = &EndpointContext->Ep;
  584. nextEp = Ep->next_ep;
  585. if (nextEp) {
  586. nextContext = CONTAINING_RECORD(nextEp,
  587. struct _USB2LIB_ENDPOINT_CONTEXT,
  588. Ep);
  589. // assert(EndpointContext->Sig == SIG_LIB_EP);
  590. return nextContext->RebalanceContext;
  591. } else {
  592. return NULL;
  593. }
  594. }