Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | \ ========== Copyright Header Begin ========================================== |
2 | \ | |
3 | \ Hypervisor Software File: nq.fth | |
4 | \ | |
5 | \ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | \ | |
7 | \ - Do no alter or remove copyright notices | |
8 | \ | |
9 | \ - Redistribution and use of this software in source and binary forms, with | |
10 | \ or without modification, are permitted provided that the following | |
11 | \ conditions are met: | |
12 | \ | |
13 | \ - Redistribution of source code must retain the above copyright notice, | |
14 | \ this list of conditions and the following disclaimer. | |
15 | \ | |
16 | \ - Redistribution in binary form must reproduce the above copyright notice, | |
17 | \ this list of conditions and the following disclaimer in the | |
18 | \ documentation and/or other materials provided with the distribution. | |
19 | \ | |
20 | \ Neither the name of Sun Microsystems, Inc. or the names of contributors | |
21 | \ may be used to endorse or promote products derived from this software | |
22 | \ without specific prior written permission. | |
23 | \ | |
24 | \ This software is provided "AS IS," without a warranty of any kind. | |
25 | \ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
26 | \ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
27 | \ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
28 | \ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
29 | \ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
30 | \ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
31 | \ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
32 | \ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
33 | \ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
34 | \ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
35 | \ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
36 | \ | |
37 | \ You acknowledge that this software is not designed, licensed or | |
38 | \ intended for use in the design, construction, operation or maintenance of | |
39 | \ any nuclear facility. | |
40 | \ | |
41 | \ ========== Copyright Header End ============================================ | |
42 | id: @(#)nq.fth 1.4 99/09/21 | |
43 | purpose: | |
44 | copyright: Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved | |
45 | ||
46 | \ Look down the q until the next-endpoint is 0 (which will be on the last | |
47 | \ descriptor), or next-endpoint points to a dummy q (which will be on the | |
48 | \ last of one of the interrupt q's). | |
49 | : find-last-endpoint ( addr1 -- addr2 ) | |
50 | begin ( e-addr ) | |
51 | dup next-endpoint@ ( curr-addr nxt-addr ) | |
52 | dup if dev>virt dummy-endpoint? invert then ( e-addr use-next? ) | |
53 | while next-endpoint@ dev>virt | |
54 | repeat | |
55 | ; | |
56 | ||
57 | \ Stick new endpoints only on the end of the q. So even for the interrupt | |
58 | \ q's, any following endpoint does not need its prev-endpoint pointer changed, | |
59 | \ since it is head of queue and has prev = 0. | |
60 | \ So this endpoint descriptor and the previous one are the ones that must be | |
61 | \ synced. | |
62 | : e>q ( addr q-id -- ) \ endpoint to q-id | |
63 | 2dup swap q-id ! | |
64 | interrupt-dummy find-last-endpoint >r | |
65 | dup r@ next-endpoint@ \ there are other q's, so | |
66 | swap next-endpoint! \ copy next into this one | |
67 | r> swap 2dup prev-endpoint ! \ hook this one in | |
68 | 2dup virt>dev swap next-endpoint! ( prev-ep cur-ep ) | |
69 | sync-endpoint sync-endpoint | |
70 | ; | |
71 | ||
72 | \ XXX need to carry data toggle from endpoint descriptor forward from | |
73 | \ transfer descriptor to transfer descriptor? it is returned in the | |
74 | \ endpoint descriptor. | |
75 | \ No, let the transfer descriptor handle it -- which means the child | |
76 | \ device, essentially. This works for control transactions, but seems | |
77 | \ cumbersome for interrupt transactions. | |
78 | ||
79 | \ The endpoint descriptors must have at least a null transfer descriptor | |
80 | \ attached before putting the endpoint desc. on a queue to avoid a race | |
81 | \ condition when queueing real transfer descriptors. | |
82 | \ The last transfer descriptor is not touched by the chip (ohci 4.6). | |
83 | ||
84 | : make-transfer-d ( -- addr ) | |
85 | /transfer get-chunk | |
86 | ; | |
87 | ||
88 | \ Assume no endpoint will be put on q unless it has a real transfer | |
89 | \ descriptor. | |
90 | \ endpoint direction from transfer descriptors | |
91 | : make-endpoint ( speed max-pkt endpt usb-addr -- addr ) | |
92 | /endpoint get-chunk >r | |
93 | swap 7 lshift or swap h# 10 lshift or | |
94 | swap if h# 2000 or then | |
95 | r@ endpoint-control le-l! r> | |
96 | \ Make empty transfer-d and attach. | |
97 | make-transfer-d virt>dev | |
98 | tuck over td-tail! tuck td-head! | |
99 | ; | |
100 | ||
101 | ||
102 | : transfer-bits>copy-in? ( transfer-d endp-bits -- copy-in? dummy ) | |
103 | swap transfer-control le-l@ | |
104 | d# 19 rshift 3 and \ dp bits | |
105 | 2 < \ setup or out | |
106 | swap | |
107 | ; | |
108 | ||
109 | : copy-in? ( transfer-d -- copy-in? ) | |
110 | dup my-endpoint @ endpoint-control le-l@ | |
111 | d# 11 rshift 3 and \ d bits | |
112 | case 1 of drop true endof | |
113 | 2 of drop false endof | |
114 | transfer-bits>copy-in? | |
115 | endcase | |
116 | ; | |
117 | ||
118 | \ for setup packets or out packets -- need to look at endp-d | |
119 | : copy-in ( transfer-d -- ) | |
120 | dup copy-in? if | |
121 | dup caller-data @ | |
122 | over my-data @ | |
123 | rot caller-count @ | |
124 | move | |
125 | else drop | |
126 | then | |
127 | ; | |
128 | ||
129 | : copy-for-me ( transfer-d -- ) | |
130 | dup caller-count @ ?dup if \ a real transfer | |
131 | get-chunk over my-data ! \ make a copy buffer | |
132 | copy-in | |
133 | else drop | |
134 | then | |
135 | ; | |
136 | ||
137 | : my-dev-data ( transfer-d -- dev-data-adr ) | |
138 | my-data @ dup if virt>dev then | |
139 | ; | |
140 | ||
141 | : buffer-bounds ( transfer-d -- ) | |
142 | dup my-dev-data over curr-buffer le-l! | |
143 | dup my-dev-data over caller-count @ + | |
144 | dup if 1- then \ may be a 0 len transfer | |
145 | swap buffer-end le-l! | |
146 | ; | |
147 | ||
148 | \ XXX don't allow caller-addr <>0 with caller-len = 0. the other way | |
149 | \ is ok (for interrupt transfers). | |
150 | \ bufferRounding, no DelayInterrupt | |
151 | : fill-transfer-d ( caller-data-adr buf-len toggle pid-code transfer-adr -- ) | |
152 | >r | |
153 | d# 19 lshift h# 4.0000 or swap d# 24 lshift or | |
154 | r@ transfer-control le-l! ( caller-addr len ) ( R: transfer-d ) | |
155 | r@ caller-count ! | |
156 | r@ caller-data ! | |
157 | r@ copy-for-me | |
158 | r> buffer-bounds | |
159 | ; | |
160 | ||
161 | ||
162 | \ buffer to hold offsets so they can be reversed; 100 nominal | |
163 | h# 100 buffer: isoc-temp \ must be global | |
164 | ||
165 | : make-isoc-offsets ( dev-badr blen -- offset7 ... offset0 ) | |
166 | isoc-temp h# 100 erase | |
167 | isoc-temp 0 2swap ( buf-adr index dev-badr blen ) | |
168 | swap h# fff and swap \ only bottom 12 bits needed | |
169 | bounds do ( buf-adr index ) | |
170 | 2dup na+ | |
171 | i swap ! | |
172 | 1+ | |
173 | d# 1023 +loop | |
174 | drop ( buf-adr ) | |
175 | 0 7 do | |
176 | dup i na+ @ swap | |
177 | -1 +loop ( off7 ... off0 buf-adr ) | |
178 | drop | |
179 | ; | |
180 | ||
181 | : fill-isoc-offsets ( offset7 ... offset0 transfer-d -- ) | |
182 | 4 0 do | |
183 | >r | |
184 | swap wljoin | |
185 | r@ offset0 i 4 * + le-l! | |
186 | r> | |
187 | loop | |
188 | drop | |
189 | ; | |
190 | ||
191 | : (set-isoc-offsets) ( dev-badr blen transfer-d -- ) | |
192 | >r | |
193 | dup d# 1023 /mod | |
194 | swap if 1+ then | |
195 | 1- 7 and d# 24 lshift | |
196 | r@ isoc-control tuck | |
197 | le-l@ h# f8ff.ffff and or | |
198 | swap le-l! ( dev-badr blen ) ( R: transfer-d ) | |
199 | make-isoc-offsets | |
200 | r> fill-isoc-offsets \ fill in the offsets from offsets on stack | |
201 | ; | |
202 | ||
203 | \ ohci 4.3.2.3.5.4 says max isoc data packet 3ff (1023) bytes, but the | |
204 | \ transfer descriptor seems to allow 400 (1024) bytes. | |
205 | \ uhci 3.2.3 says max len is 1023 also, and says it's in the usb spec. | |
206 | \ it also says that 1280 is the max theoretical for one frame. | |
207 | \ usb 5.6.3 says 1023 bytes max | |
208 | \ each page 4K, two pages max. | |
209 | : set-isoc-offsets ( dev-badr blen transfer-d -- ) | |
210 | over if (set-isoc-offsets) then | |
211 | ; | |
212 | ||
213 | \ XXX isoc transfer direction seems to be in the endpoint, not the transfer | |
214 | \ no DelayInterrupt | |
215 | : fill-isoc-transfer-d ( f# caller-d dev-badr blen transfer-d -- ) | |
216 | >r r@ /transfer erase | |
217 | over h# ffff.f000 and | |
218 | r@ buff-page le-l! | |
219 | 2dup + over if 1- then \ could be 0 len transfer | |
220 | r@ buffer-end le-l! | |
221 | r@ set-isoc-offsets ( f# caller-d ) ( R: transfer-d ) | |
222 | r@ caller-data ! | |
223 | h# ffff and \ only least 16 bits of frame used | |
224 | r> isoc-control tuck le-l@ | |
225 | h# ffff.0000 and | |
226 | or swap le-l! | |
227 | ; | |
228 | ||
229 | ||
230 | \ The data gets copied into the last transfer descriptor, a new empty | |
231 | \ descriptor gets hooked to it, then the tail pointer is updated to point | |
232 | \ to the new empty one. | |
233 | ||
234 | \ transfer to endpoint q: | |
235 | : t>endq ( caller-data-adr buf-len toggle pid-code endpoint-addr -- ) | |
236 | ||
237 | \ Find last transfer d on q: | |
238 | >r r@ td-tail@ dev>virt ( ... tail-trans-d-addr ) | |
239 | r@ over my-endpoint ! | |
240 | ||
241 | \ Put transfer info into the last one on the q: | |
242 | dup >r fill-transfer-d r> ( tail-d-addr ) ( R: endpt-addr ) | |
243 | ||
244 | make-transfer-d ( tail-addr new-last-addr ) | |
245 | ||
246 | \ Hook new one into old tail d: | |
247 | virt>dev tuck swap next-transfer le-l! ( tail-addr new-last new-dev-addr ) | |
248 | 1 r@ transfer-count +! \ bump transfer count | |
249 | r> td-tail! \ Update td-tail in the endpoint d | |
250 | sync-mem | |
251 | ; | |
252 | ||
253 | \ XXX no dev-badr | |
254 | \ isoc transfer to endpoint q: | |
255 | : isoct>endq ( f# caller-d dev-badr blen endp-d -- ) | |
256 | >r r@ td-tail@ dev>virt ( ... tail-transfer-d ) | |
257 | dup >r fill-isoc-transfer-d ( -- ) ( R: endp-d tail-d ) | |
258 | r> dup r@ swap my-endpoint ! | |
259 | make-transfer-d ( tail-addr new-last-addr ) | |
260 | ||
261 | \ Hook new one into old tail d: | |
262 | virt>dev tuck swap next-transfer le-l! ( tail-addr new-last new-dev-addr ) | |
263 | 1 r@ transfer-count +! \ bump transfer count | |
264 | r> td-tail! \ Update td-tail in the endpoint d | |
265 | \ XXX should fill my-endpoint for the new last descriptor. | |
266 | sync-mem | |
267 | ; | |
268 | ||
269 | ||
270 | \ Two alternative strategies: | |
271 | ||
272 | \ 1. Each request from child node gets essentially one endpoint with a string | |
273 | \ of transfers. when transfer finishes, retire the endpoint. | |
274 | \ 2. the same endpoint can be used for more than one request from child node. | |
275 | ||
276 | \ I lean towards 1. This means that it probably makes most sense for the | |
277 | \ endpoint to have all its transfer d's installed before putting the endpoint | |
278 | \ on the q. | |
279 | ||
280 | ||
281 | \ : low-bits? ( n high-bit -- low-bits? ) | |
282 | \ xor | |
283 | \ ; | |
284 | ||
285 | : find-high-bit ( n1 -- n2 ) | |
286 | dup h# 20 and if drop h# 20 exit then | |
287 | dup h# 10 and if drop h# 10 exit then | |
288 | dup 8 and if drop 8 exit then | |
289 | dup 4 and if drop 4 exit then | |
290 | 2 and if 2 else 1 then | |
291 | ; | |
292 | ||
293 | \ : next-power-of-two ( n -- power ) | |
294 | \ dup find-high-bit | |
295 | \ tuck low-bits? if 1+ then | |
296 | \ ; | |
297 | ||
298 | : prev-power-of-two ( n -- power ) | |
299 | find-high-bit | |
300 | ; | |
301 | ||
302 | \ Use power to find the correct range of q's. Maybe use ms to "hash" into | |
303 | \ the range for the specific q. Should also take into account the loading | |
304 | \ of the various q's. | |
305 | \ XXX Stupid algorithm, pick the last q of the right priority. | |
306 | \ It always works: | |
307 | : choose-q ( ms power -- [q#] q-found? ) | |
308 | nip d# 63 swap - | |
309 | true | |
310 | ; | |
311 | ||
312 | \ Pick the q to use based on ms, the maximum polling interval, and the | |
313 | \ loading of the various q's with that interval. | |
314 | \ q-found? is non-false if it was able to pick a q. If q-found? is non-false, | |
315 | \ q# is returned also. | |
316 | \ Use semi-brute-force: The numbers are only up to 32, so for the valid | |
317 | \ ones, 1) mask for the high bit, 2) check for any low bits, and 3) if there | |
318 | \ are low bits, double the high bit. This gets to the next greater | |
319 | \ power-of-two. | |
320 | \ Previous algorithm uses minimum polling interval. For maximum polling | |
321 | \ interval, semi-brute-force: 1) mask for the high bit. This gets to the | |
322 | \ previous power-of-two. | |
323 | : pick-q ( ms -- [q#] q-found? ) | |
324 | dup 0<= over d# 32 > or if \ out of range | |
325 | drop 0 | |
326 | else | |
327 | dup prev-power-of-two ( ms power ) | |
328 | choose-q | |
329 | then | |
330 | ; | |
331 | ||
332 | \ XXX There should be a bandwidth check when adding and deleting transfers and | |
333 | \ endpoints. And picking q's? |