Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. ; Implementation of periodic transaction scheduler for USB.
  2. ; Bandwidth dedicated to periodic transactions is limited, so
  3. ; different pipes should be scheduled as uniformly as possible.
  4.  
  5. ; USB1 scheduler.
  6. ; Algorithm is simple:
  7. ; when adding a pipe, optimize the following quantity:
  8. ;  * for every millisecond, take all bandwidth scheduled to periodic transfers,
  9. ;  * calculate maximum over all milliseconds,
  10. ;  * select a variant which minimizes that maximum;
  11. ; when removing a pipe, do nothing (except for bookkeeping).
  12.  
  13. ; sanity check: structures in UHCI and OHCI should be the same
  14. if (sizeof.ohci_static_ep=sizeof.uhci_static_ep)&(ohci_static_ep.SoftwarePart=uhci_static_ep.SoftwarePart)&(ohci_static_ep.NextList=uhci_static_ep.NextList)
  15. ; Select a list for a new pipe.
  16. ; in: esi -> usb_controller, maxpacket, type, interval can be found in the stack
  17. ; in: ecx = 2 * maximal interval = total number of periodic lists + 1
  18. ; in: edx -> {u|o}hci_static_ep for the first list
  19. ; in: eax -> byte past {u|o}hci_static_ep for the last list in the first group
  20. ; out: edx -> usb_static_ep for the selected list or zero if failed
  21. proc usb1_select_interrupt_list
  22. ; inherit some variables from usb_open_pipe
  23. virtual at ebp-8
  24. .bandwidth      dd      ?
  25. .target         dd      ?
  26.                 dd      ?
  27.                 dd      ?
  28. .config_pipe    dd      ?
  29. .endpoint       dd      ?
  30. .maxpacket      dd      ?
  31. .type           dd      ?
  32. .interval       dd      ?
  33. end virtual
  34.         push    ebx edi         ; save used registers to be stdcall
  35.         push    eax             ; save eax for checks in step 3
  36. ; 1. Only intervals 2^k ms can be supported.
  37. ; The core specification says that the real interval should not be greater
  38. ; than the interval given by the endpoint descriptor, but can be less.
  39. ; Determine the actual interval as 2^k ms.
  40.         mov     eax, ecx
  41. ; 1a. Set [.interval] to 1 if it was zero; leave it as is otherwise
  42.         cmp     [.interval], 1
  43.         adc     [.interval], 0
  44. ; 1b. Divide ecx by two while it is strictly greater than [.interval].
  45. @@:
  46.         shr     ecx, 1
  47.         cmp     [.interval], ecx
  48.         jb      @b
  49. ; ecx = the actual interval
  50. ;
  51. ; For example, let ecx = 8, eax = 64.
  52. ; The scheduler space is 32 milliseconds,
  53. ; we need to schedule something every 8 ms;
  54. ; there are 8 variants: schedule at times 0,8,16,24,
  55. ; schedule at times 1,9,17,25,..., schedule at times 7,15,23,31.
  56. ; Now concentrate: there are three nested loops,
  57. ; * the innermost loop calculates the total periodic bandwidth scheduled
  58. ;   in the given millisecond,
  59. ; * the intermediate loop calculates the maximum over all milliseconds
  60. ;   in the given variant, that is the quantity we're trying to minimize,
  61. ; * the outermost loop checks all variants.
  62. ; 2. Calculate offset between the first list and the first list for the
  63. ; selected interval, in bytes; save in the stack for step 4.
  64.         sub     eax, ecx
  65.         sub     eax, ecx
  66.         imul    eax, sizeof.ohci_static_ep
  67.         push    eax
  68.         imul    ebx, ecx, sizeof.ohci_static_ep
  69. ; 3. Select the best variant.
  70. ; 3a. The outermost loop.
  71. ; Prepare for the loop: set the current optimal bandwidth to maximum
  72. ; possible value (so that any variant will pass the first comparison),
  73. ; calculate delta for the intermediate loop.
  74.         or      [.bandwidth], -1
  75. .varloop:
  76. ; 3b. The intermediate loop.
  77. ; Prepare for the loop: set the maximum to be calculated to zero,
  78. ; save counter of the outermost loop.
  79.         xor     edi, edi
  80.         push    edx
  81. virtual at esp
  82. .cur_variant    dd      ?       ; step 3b
  83. .result_delta   dd      ?       ; step 2
  84. .group1_limit   dd      ?       ; function prolog
  85. end virtual
  86. .calc_max_bandwidth:
  87. ; 3c. The innermost loop. Sum over all lists.
  88.         xor     eax, eax
  89.         push    edx
  90. .calc_bandwidth:
  91.         add     eax, [edx+ohci_static_ep.SoftwarePart+usb_static_ep.Bandwidth]
  92.         mov     edx, [edx+ohci_static_ep.NextList]
  93.         test    edx, edx
  94.         jnz     .calc_bandwidth
  95.         pop     edx
  96. ; 3d. The intermediate loop continued: update maximum.
  97.         cmp     eax, edi
  98.         jb      @f
  99.         mov     edi, eax
  100. @@:
  101. ; 3e. The intermediate loop continued: advance counter.
  102.         add     edx, ebx
  103.         cmp     edx, [.group1_limit]
  104.         jb      .calc_max_bandwidth
  105. ; 3e. The intermediate loop done: restore counter of the outermost loop.
  106.         pop     edx
  107. ; 3f. The outermost loop continued: if the current variant is
  108. ; better (maybe not strictly) then the previous optimum, update
  109. ; the optimal bandwidth and resulting list.
  110.         cmp     edi, [.bandwidth]
  111.         ja      @f
  112.         mov     [.bandwidth], edi
  113.         mov     [.target], edx
  114. @@:
  115. ; 3g. The outermost loop continued: advance counter.
  116.         add     edx, sizeof.ohci_static_ep
  117.         dec     ecx
  118.         jnz     .varloop
  119. ; 4. Get the pointer to the best list.
  120.         pop     edx             ; restore value from step 2
  121.         pop     eax             ; purge stack var from prolog
  122.         add     edx, [.target]
  123. ; 5. Calculate bandwidth for the new pipe.
  124.         mov     eax, [.maxpacket]       ; TODO: calculate real bandwidth
  125.         and     eax, (1 shl 11) - 1
  126. ; 6. TODO: check that bandwidth for the new pipe plus old bandwidth
  127. ; still fits to maximum allowed by the core specification.
  128. ; 7. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return.
  129.         add     edx, ohci_static_ep.SoftwarePart
  130.         add     [edx+usb_static_ep.Bandwidth], eax
  131.         pop     edi ebx         ; restore used registers to be stdcall
  132.         ret
  133. endp
  134. ; sanity check, part 2
  135. else
  136. .err select_interrupt_list must be different for UHCI and OHCI
  137. end if
  138.  
  139. ; Pipe is removing, update the corresponding lists.
  140. ; We do not reorder anything, so just update book-keeping variable
  141. ; in the list header.
  142. proc usb1_interrupt_list_unlink
  143. virtual at esp
  144.                 dd      ?       ; return address
  145. .maxpacket      dd      ?
  146. .lowspeed       db      ?
  147. .direction      db      ?
  148.                 rb      2
  149. end virtual
  150. ; find list header
  151.         mov     edx, ebx
  152. @@:
  153.         mov     edx, [edx+usb_pipe.NextVirt]
  154.         cmp     [edx+usb_pipe.Controller], esi
  155.         jnz     @b
  156. ; subtract pipe bandwidth
  157. ; TODO: calculate real bandwidth
  158.         mov     eax, [.maxpacket]
  159.         and     eax, (1 shl 11) - 1
  160.         sub     [edx+usb_static_ep.Bandwidth], eax
  161.         ret     8
  162. endp
  163.  
  164. ; USB2 scheduler.
  165. ; There are two parts: high-speed pipes and split-transaction pipes.
  166. ; Split-transaction scheduler is currently a stub.
  167. ; High-speed scheduler uses the same algorithm as USB1 scheduler:
  168. ; when adding a pipe, optimize the following quantity:
  169. ;  * for every microframe, take all bandwidth scheduled to periodic transfers,
  170. ;  * calculate maximum over all microframe,
  171. ;  * select a variant which minimizes that maximum;
  172. ; when removing a pipe, do nothing (except for bookkeeping).
  173. ; in: esi -> usb_controller
  174. ; out: edx -> usb_static_ep, eax = S-Mask
  175. proc ehci_select_hs_interrupt_list
  176. ; inherit some variables from usb_open_pipe
  177. virtual at ebp-12
  178. .targetsmask    dd      ?
  179. .bandwidth      dd      ?
  180. .target         dd      ?
  181.                 dd      ?
  182.                 dd      ?
  183. .config_pipe    dd      ?
  184. .endpoint       dd      ?
  185. .maxpacket      dd      ?
  186. .type           dd      ?
  187. .interval       dd      ?
  188. end virtual
  189. ; prolog, initialize local vars
  190.         or      [.bandwidth], -1
  191.         or      [.target], -1
  192.         or      [.targetsmask], -1
  193.         push    ebx edi         ; save used registers to be stdcall
  194. ; 1. In EHCI, every list describes one millisecond = 8 microframes.
  195. ; Thus, there are two significantly different branches:
  196. ; for pipes with interval >= 8 microframes, advance to 2,
  197. ; for pipes which should be planned in every frame (one or more microframes),
  198. ; go to 9.
  199. ; Note: the actual interval for high-speed devices is 2^([.interval]-1),
  200. ; (the core specification forbids [.interval] == 0)
  201.         mov     ecx, [.interval]
  202.         dec     ecx
  203.         cmp     ecx, 3
  204.         jb      .every_frame
  205. ; 2. Determine the actual interval in milliseconds.
  206.         sub     ecx, 3
  207.         cmp     ecx, 5  ; maximum 32ms
  208.         jbe     @f
  209.         push    5
  210.         pop     ecx
  211. @@:
  212. ; There are four nested loops,
  213. ; * Loop #4 (the innermost one) calculates the total periodic bandwidth
  214. ;   scheduled in the given microframe of the given millisecond.
  215. ; * Loop #3 calculates the maximum over all milliseconds
  216. ;   in the given variant, that is the quantity we're trying to minimize.
  217. ; * Loops #1 and #2 check all variants;
  218. ;   loop #1 is responsible for the target millisecond,
  219. ;   loop #2 is responsible for the microframe within millisecond.
  220. ; 3. Prepare for loops.
  221. ; ebx = number of iterations of loop #1
  222. ; [esp] = delta of counter for loop #3, in bytes
  223. ; [esp+4] = delta between the first group and the target group, in bytes
  224.         push    1
  225.         pop     ebx
  226.         push    sizeof.ehci_static_ep
  227.         pop     edx
  228.         shl     ebx, cl
  229.         shl     edx, cl
  230.         mov     eax, 64*sizeof.ehci_static_ep
  231.         sub     eax, edx
  232.         sub     eax, edx
  233.         push    eax
  234.         push    edx
  235. ; 4. Select the best variant.
  236. ; 4a. Loop #1: initialize counter = pointer to ehci_static_ep for
  237. ; the target millisecond in the first group.
  238.         lea     edx, [esi+ehci_controller.IntEDs-sizeof.ehci_controller]
  239. .varloop0:
  240. ; 4b. Loop #2: initialize counter = microframe within the target millisecond.
  241.         xor     ecx, ecx
  242. .varloop:
  243. ; 4c. Loop #3: save counter of loop #1,
  244. ; initialize counter with the value of loop #1 counter,
  245. ; initialize maximal bandwidth = zero.
  246.         xor     edi, edi
  247.         push    edx
  248. virtual at esp
  249. .saved_counter1         dd      ?       ; step 4c
  250. .loop3_delta            dd      ?       ; step 3
  251. .target_delta           dd      ?       ; step 3
  252. end virtual
  253. .calc_max_bandwidth:
  254. ; 4d. Loop #4: initialize counter with the value of loop #3 counter,
  255. ; initialize total bandwidth = zero.
  256.         xor     eax, eax
  257.         push    edx
  258. .calc_bandwidth:
  259. ; 4e. Loop #4: add the bandwidth from the current list
  260. ; and advance to the next list, while there is one.
  261.         add     ax, [edx+ehci_static_ep.Bandwidths+ecx*2]
  262.         mov     edx, [edx+ehci_static_ep.NextList]
  263.         test    edx, edx
  264.         jnz     .calc_bandwidth
  265. ; 4f. Loop #4 end: restore counter of loop #3.
  266.         pop     edx
  267. ; 4g. Loop #3: update maximal bandwidth.
  268.         cmp     eax, edi
  269.         jb      @f
  270.         mov     edi, eax
  271. @@:
  272. ; 4h. Loop #3: advance the counter and repeat while within the first group.
  273.         lea     eax, [esi+ehci_controller.IntEDs+32*sizeof.ehci_static_ep-sizeof.ehci_controller]
  274.         add     edx, [.loop3_delta]
  275.         cmp     edx, eax
  276.         jb      .calc_max_bandwidth
  277. ; 4i. Loop #3 end: restore counter of loop #1.
  278.         pop     edx
  279. ; 4j. Loop #2: if the current variant is better (maybe not strictly)
  280. ; then the previous optimum, update the optimal bandwidth and the target.
  281.         cmp     edi, [.bandwidth]
  282.         ja      @f
  283.         mov     [.bandwidth], edi
  284.         mov     [.target], edx
  285.         push    1
  286.         pop     eax
  287.         shl     eax, cl
  288.         mov     [.targetsmask], eax
  289. @@:
  290. ; 4k. Loop #2: continue 8 times for every microframe.
  291.         inc     ecx
  292.         cmp     ecx, 8
  293.         jb      .varloop
  294. ; 4l. Loop #1: advance counter and repeat ebx times,
  295. ; ebx was calculated in step 3.
  296.         add     edx, sizeof.ehci_static_ep
  297.         dec     ebx
  298.         jnz     .varloop0
  299. ; 5. Get the pointer to the best list.
  300.         pop     edx             ; restore value from step 3
  301.         pop     edx             ; get delta calculated in step 3
  302.         add     edx, [.target]
  303. ; 6. Calculate bandwidth for the new pipe.
  304. ; TODO1: calculate real bandwidth
  305.         mov     eax, [.maxpacket]
  306.         mov     ecx, eax
  307.         and     eax, (1 shl 11) - 1
  308.         shr     ecx, 11
  309.         inc     ecx
  310.         and     ecx, 3
  311.         imul    eax, ecx
  312. ; 7. TODO2: check that bandwidth for the new pipe plus old bandwidth
  313. ; still fits to maximum allowed by the core specification
  314. ; current [.bandwidth] + new bandwidth <= limit;
  315. ; USB2 specification allows maximum 60000*80% bit times for periodic microframe
  316. ; 8. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return.
  317.         mov     ecx, [.targetsmask]
  318.         add     [edx+ehci_static_ep.Bandwidths+ecx*2], ax
  319.         add     edx, ehci_static_ep.SoftwarePart
  320.         push    1
  321.         pop     eax
  322.         shl     eax, cl
  323.         pop     edi ebx         ; restore used registers to be stdcall
  324.         ret
  325. .every_frame:
  326. ; The pipe should be scheduled every frame in two or more microframes.
  327. ; 9. Calculate maximal bandwidth for every microframe: three nested loops.
  328. ; 9a. The outermost loop: ebx = microframe to calculate.
  329.         xor     ebx, ebx
  330. .calc_all_bandwidths:
  331. ; 9b. The intermediate loop:
  332. ; edx = pointer to ehci_static_ep in the first group, [esp] = counter,
  333. ; edi = maximal bandwidth
  334.         lea     edx, [esi+ehci_controller.IntEDs-sizeof.ehci_controller]
  335.         xor     edi, edi
  336.         push    32
  337. .calc_max_bandwidth2:
  338. ; 9c. The innermost loop: calculate bandwidth for the given microframe
  339. ; in the given frame.
  340.         xor     eax, eax
  341.         push    edx
  342. .calc_bandwidth2:
  343.         add     ax, [edx+ehci_static_ep.Bandwidths+ebx*2]
  344.         mov     edx, [edx+ehci_static_ep.NextList]
  345.         test    edx, edx
  346.         jnz     .calc_bandwidth2
  347.         pop     edx
  348. ; 9d. The intermediate loop continued: update maximal bandwidth.
  349.         cmp     eax, edi
  350.         jb      @f
  351.         mov     edi, eax
  352. @@:
  353.         add     edx, sizeof.ehci_static_ep
  354.         dec     dword [esp]
  355.         jnz     .calc_max_bandwidth2
  356.         pop     eax
  357. ; 9e. Push the calculated maximal bandwidth and continue the outermost loop.
  358.         push    edi
  359.         inc     ebx
  360.         cmp     ebx, 8
  361.         jb      .calc_all_bandwidths
  362. virtual at esp
  363. .bandwidth7     dd      ?
  364. .bandwidth6     dd      ?
  365. .bandwidth5     dd      ?
  366. .bandwidth4     dd      ?
  367. .bandwidth3     dd      ?
  368. .bandwidth2     dd      ?
  369. .bandwidth1     dd      ?
  370. .bandwidth0     dd      ?
  371. end virtual
  372. ; 10. Select the best variant.
  373. ; edx = S-Mask = bitmask of scheduled microframes
  374.         push    0x11
  375.         pop     edx
  376.         cmp     ecx, 1
  377.         ja      @f
  378.         mov     dl, 0x55
  379.         jz      @f
  380.         mov     dl, 0xFF
  381. @@:
  382. ; try all variants edx, edx shl 1, edx shl 2, ...
  383. ; until they fit in the lower byte (8 microframes per frame)
  384. .select_best_mframe:
  385.         xor     edi, edi
  386.         mov     ecx, edx
  387.         mov     eax, esp
  388. .calc_mframe:
  389.         add     cl, cl
  390.         jnc     @f
  391.         cmp     edi, [eax]
  392.         jae     @f
  393.         mov     edi, [eax]
  394. @@:
  395.         add     eax, 4
  396.         test    cl, cl
  397.         jnz     .calc_mframe
  398.         cmp     [.bandwidth], edi
  399.         jb      @f
  400.         mov     [.bandwidth], edi
  401.         mov     [.targetsmask], edx
  402. @@:
  403.         add     dl, dl
  404.         jnc     .select_best_mframe
  405. ; 11. Restore stack after step 9.
  406.         add     esp, 8*4
  407. ; 12. Get the pointer to the target list (responsible for every microframe).
  408.         lea     edx, [esi+ehci_controller.IntEDs.SoftwarePart+62*sizeof.ehci_static_ep-sizeof.ehci_controller]
  409. ; 13. TODO1: calculate real bandwidth.
  410.         mov     eax, [.maxpacket]
  411.         mov     ecx, eax
  412.         and     eax, (1 shl 11) - 1
  413.         shr     ecx, 11
  414.         inc     ecx
  415.         and     ecx, 3
  416.         imul    eax, ecx
  417. ; 14. TODO2: check that current [.bandwidth] + new bandwidth <= limit;
  418. ; USB2 specification allows maximum 60000*80% bit times for periodic microframe.
  419. ; Update bandwidths including the new pipe.
  420.         mov     ecx, [.targetsmask]
  421.         lea     edi, [edx+ehci_static_ep.Bandwidths-ehci_static_ep.SoftwarePart]
  422. .update_bandwidths:
  423.         shr     ecx, 1
  424.         jnc     @f
  425.         add     [edi], ax
  426. @@:
  427.         add     edi, 2
  428.         test    ecx, ecx
  429.         jnz     .update_bandwidths
  430. ; 15. Return target list and target S-Mask.
  431.         mov     eax, [.targetsmask]
  432.         pop     edi ebx         ; restore used registers to be stdcall
  433.         ret
  434. endp
  435.  
  436. ; Pipe is removing, update the corresponding lists.
  437. ; We do not reorder anything, so just update book-keeping variable
  438. ; in the list header.
  439. proc ehci_hs_interrupt_list_unlink
  440. ; get target list
  441.         mov     edx, [ebx+ehci_pipe.BaseList-ehci_pipe.SoftwarePart]
  442. ; TODO: calculate real bandwidth
  443.         movzx   eax, word [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart+2]
  444.         mov     ecx, [ebx+ehci_pipe.Flags-ehci_pipe.SoftwarePart]
  445.         and     eax, (1 shl 11) - 1
  446.         shr     ecx, 30
  447.         imul    eax, ecx
  448.         movzx   ecx, byte [ebx+ehci_pipe.Flags-ehci_pipe.SoftwarePart]
  449.         add     edx, ehci_static_ep.Bandwidths - ehci_static_ep.SoftwarePart
  450. ; update bandwidth
  451. .dec_bandwidth:
  452.         shr     ecx, 1
  453.         jnc     @f
  454.         sub     [edx], ax
  455. @@:
  456.         add     edx, 2
  457.         test    ecx, ecx
  458.         jnz     .dec_bandwidth
  459. ; return
  460.         ret
  461. endp
  462.  
  463. uglobal
  464. ehci_last_fs_alloc      dd      ?
  465. endg
  466.  
  467. ; This needs to be rewritten. Seriously.
  468. ; It schedules everything to the first microframe of some frame,
  469. ; frame is spinned out of thin air.
  470. ; This works while you have one keyboard and one mouse...
  471. ; maybe even ten keyboards and ten mice... but give any serious stress,
  472. ; and this would break.
  473. proc ehci_select_fs_interrupt_list
  474. virtual at ebp-12
  475. .targetsmask    dd      ?
  476. .bandwidth      dd      ?
  477. .target         dd      ?
  478.                 dd      ?
  479.                 dd      ?
  480. .config_pipe    dd      ?
  481. .endpoint       dd      ?
  482. .maxpacket      dd      ?
  483. .type           dd      ?
  484. .interval       dd      ?
  485. end virtual
  486.         cmp     [.interval], 1
  487.         adc     [.interval], 0
  488.         mov     ecx, 64
  489.         mov     eax, ecx
  490. @@:
  491.         shr     ecx, 1
  492.         cmp     [.interval], ecx
  493.         jb      @b
  494.         sub     eax, ecx
  495.         sub     eax, ecx
  496.         dec     ecx
  497.         and     ecx, [ehci_last_fs_alloc]
  498.         inc     [ehci_last_fs_alloc]
  499.         add     eax, ecx
  500.         imul    eax, sizeof.ehci_static_ep
  501.         lea     edx, [esi+ehci_controller.IntEDs.SoftwarePart+eax-sizeof.ehci_controller]
  502.         mov     ax, 1C01h
  503.         ret
  504. endp
  505.  
  506. proc ehci_fs_interrupt_list_unlink
  507.         ret
  508. endp
  509.