Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3555 Serge 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