Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3520 clevermous 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