Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | \ ========== Copyright Header Begin ========================================== |
2 | \ | |
3 | \ Hypervisor Software File: usb.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: @(#)usb.fth 1.16 03/05/12 | |
43 | purpose: | |
44 | copyright: Copyright 1997-2000, 2003 Sun Microsystems, Inc. All Rights Reserved | |
45 | copyright: Use is subject to license terms. | |
46 | ||
47 | external | |
48 | ||
49 | ||
50 | \ Code for the request looks at the done-q for the endpoint. When no more | |
51 | \ transfers to go out, and both done-q's dealt with, endpoint gets torn | |
52 | \ down. Or if error recovery dumps the remaining transfers and deals with | |
53 | \ done-q's. But how does the code know whether all the replies have been | |
54 | \ received, and that there aren't more on the controller done-q waiting to | |
55 | \ be distributed? Can there be any in a transition -- processed by the | |
56 | \ controller, but not yet put on the done-q because the controller already | |
57 | \ turned the done-q over to the code, but the code has not yet acknowledged | |
58 | \ it? What do such transfer-d's look like? | |
59 | ||
60 | \ OS folks avoid the problem by running a second list structure through | |
61 | \ the transfer d's in addition to the one the chip uses, together with a | |
62 | \ re-claim mark on the transfer d's. When code decides to dump an endpoint, | |
63 | \ it marks each of the transfer d's to be re-claimed, using the non-controller | |
64 | \ list links. Then it dumps the endpoint. When transfer d's show up on the | |
65 | \ controller done q with the reclaim mark, the done-q code dumps them | |
66 | \ immediately, before attempting to assign them to an endpoint. | |
67 | ||
68 | \ Instead of double threading, try putting a count of outstanding transfer | |
69 | \ d's in the endpoint. When one is added, increment the count. When one | |
70 | \ is taken from one of the ping-pong done q's, decrement the count. Don't | |
71 | \ include the dummy transfer d in the count. If the total of the number left | |
72 | \ to do plus those on the done q's is less than the transfer d count, there | |
73 | \ are some in process (or on the controller done q). | |
74 | ||
75 | ||
76 | \ OHCI control transfers always start with setup stage and always end with | |
77 | \ a 0 len transfer status stage. There may be a data transfer stage in | |
78 | \ between. | |
79 | ||
80 | \ req-adr req-len is the control request to be sent. endpoint usb-addr is | |
81 | \ the target address. buf-adr buf-len is where accompanying data is to be | |
82 | \ sent from, or return data to be put. buf-adr = 0 and/or buf-len = 0 imply | |
83 | \ no data to be transferred. max-pkt is maximum packet to be transferred in | |
84 | \ one transfer. | |
85 | \ dir is the direction that data will flow: 0 is from target to host, 1 is | |
86 | \ from host to target. If no data transfer, dir is 1. low-speed? is true for | |
87 | \ low-speed target, false for normal speed. | |
88 | ||
89 | : execute-control ( low-speed? dir max-pkt buf-adr buf-len req-adr req-len | |
90 | endpoint usb-addr -- hw-err? | stat 0 ) | |
91 | 2swap >r >r 2swap >r >r | |
92 | 2swap swap >r -rot | |
93 | make-endpoint ( endpoint-d ) ( R: r-len r-adr b-len b-adr dir ) | |
94 | r> r> r> r> r> ( endpoint-d dir b-adr b-len r-adr r-len ) | |
95 | 2swap >r >r | |
96 | 2swap >r >r r@ ( r-adr r-len endp-d ) ( R: blen badr dir endp-d ) | |
97 | add-setup-transfer ( R: b-len b-adr dir endp-d ) | |
98 | r> r> swap r> r> | |
99 | 2swap 2dup >r >r ( b-adr b-len dir endp-d ) ( R: endp-d dir ) | |
100 | add-data-transfer ( R: endp-d dir ) | |
101 | r> r@ add-ack-transfer ( R: endp-d ) | |
102 | r> dup enque-control ( endp-d ) | |
103 | dup wait-for-last-reply ( endp-d code ) | |
104 | dup if over wipe-endpoint then | |
105 | swap deque-control ( code ) | |
106 | translate-code | |
107 | ?.error | |
108 | ; | |
109 | ||
110 | headers | |
111 | ||
112 | \ Must set skip if dir is out, as there is no data to transfer yet. | |
113 | : set-endp-dir ( dir endp-adr -- ) | |
114 | over if dup skip-endp then | |
115 | endpoint-control tuck le-l@ | |
116 | h# ffff.e7ff and | |
117 | swap if out-bits else in-bits then | |
118 | d# 11 lshift or | |
119 | swap le-l! | |
120 | ; | |
121 | ||
122 | \ XXX hack1 to set the toggle for the starting transfer-d in the endp-d | |
123 | : set-endp-toggle ( toggle endp-adr -- ) | |
124 | td-head tuck le-l@ | |
125 | swap 1+ 2 and or swap | |
126 | le-l! | |
127 | ; | |
128 | ||
129 | : set-trans-toggle ( toggle transfer-d -- ) | |
130 | transfer-control tuck le-l@ | |
131 | h# fcff.ffff and swap d# 24 lshift or | |
132 | swap le-l! | |
133 | ; | |
134 | ||
135 | : (use-endp-toggle) ( endp-d -- ) | |
136 | dup td-tail@ | |
137 | swap td-head@ dev>virt | |
138 | begin ( tail-adr next-adr ) | |
139 | 0 over set-trans-toggle | |
140 | 2dup next-transfer le-l@ <> | |
141 | while | |
142 | next-transfer le-l@ dev>virt | |
143 | repeat | |
144 | 2drop | |
145 | ; | |
146 | ||
147 | \ XXX hack2 to set the transfer-d's to use the toggle in the endpoint-d | |
148 | : use-endp-toggle ( endp-d -- ) | |
149 | dup td-head@ if (use-endp-toggle) | |
150 | else drop | |
151 | then | |
152 | ; | |
153 | ||
154 | external | |
155 | ||
156 | \ Data toggles are synchronized via a separate control endpoint transaction. | |
157 | \ OHCI setup clearing an endpoint stall sets data toggle to 0. | |
158 | \ Use set-feature to set an endpoint stall. Use clear-feature to clear an | |
159 | \ endpoint stall. | |
160 | ||
161 | \ Want to execute this one as quickly as possible, so attach it to the | |
162 | \ every-ms interrupt q. | |
163 | \ toggle1 (0 or 1) is to be used first. toggle2 is last-used. | |
164 | \ mark the toggle in the endpoint-d. | |
165 | \ XXX not good enough -- nak doesn't cause the transfer-d to be | |
166 | \ retired. need to wait only for 1 frame (nominally) rather than until reply. | |
167 | : execute-1-interrupt | |
168 | ( toggle1 low-speed? dir max-pkt buf-addr buf-len endpoint usb-adr | |
169 | -- toggle2 hw-err? | toggle2 stat 0 ) | |
170 | 2swap >r >r 2swap swap >r -rot | |
171 | ( toggle1 low-spd? max-pkt endp u-adr) ( R: blen badr dir ) | |
172 | make-endpoint ( toggle1 endp-adr ) ( R: blen badr dir ) | |
173 | tuck set-endp-toggle | |
174 | r> r> r> rot 3 pick add-data-transfer | |
175 | dup use-endp-toggle ( endp-adr ) | |
176 | dup d# 62 enque-interrupt | |
177 | dup 1-try-wait | |
178 | over end-toggle -rot | |
179 | dup if over wipe-endpoint then | |
180 | swap deque-interrupt ( toggle2 code ) | |
181 | translate-code | |
182 | ?.error | |
183 | ; | |
184 | ||
185 | ||
186 | \ for now, token will be the endpoint descriptor address | |
187 | \ token = 0 means it can't be scheduled -- possibly the interval is | |
188 | \ too great or too small. | |
189 | \ ms is the maximum gap between successive polls. | |
190 | \ use the toggle in the endpoint-d. use the dir in the endpoint-d. | |
191 | \ This uses a local buffer for data transfer. Then the report is copied into | |
192 | \ the caller's buffer by int-transaction-status, and the buffer can be re-used. | |
193 | \ disable-int-transactions throws away the local buffer. | |
194 | \ XXX buf-adr moved to int-transaction-status implies that if dir is out, | |
195 | \ there is no data to move when enable-int-transactions is executed. So the | |
196 | \ transaction should be skipped until int-transaction-status is called. | |
197 | \ So if dir is out, the endpoint descriptor must be marked SKIP, and | |
198 | \ int-transaction-status must unmark it. | |
199 | \ set dir in endpoint descriptor, mark transfer to use endpoint dir. | |
200 | : enable-int-transactions ( ms toggle low-speed? dir max-pkt buf-len | |
201 | endpoint usb-adr -- token ) | |
202 | rot >r 2swap swap >r -rot | |
203 | ( ms toggle speed max-pkt endp usb-addr ) ( R: blen dir ) | |
204 | make-endpoint ( ms toggle endp-adr ) ( R: blen dir ) | |
205 | ||
206 | tuck set-endp-toggle ( ms endp-adr ) ( R: blen dir ) | |
207 | r> over set-endp-dir \ may need to skip endpoint | |
208 | r> over caller-len ! | |
209 | dup caller-len @ get-chunk over interrupt-buf ! | |
210 | dup dup interrupt-buf @ over caller-len @ | |
211 | rot 0 swap add-data-transfer \ use target to host -- really use endp for dir | |
212 | dup use-endp-toggle ( ms endp-adr ) | |
213 | tuck swap pick-q if ( endp endp q# ) | |
214 | enque-interrupt | |
215 | else ( endp endp ) \ couldn't pick a q | |
216 | \ dump data-transfer interrupt-buf if caller-len <>0 | |
217 | \ dump ack-transfer, data-transfer | |
218 | \ clear-endpoint | |
219 | \ ( endp ) toss-endpoint | |
220 | \ XXX just to get going, since there is always a q: | |
221 | cr ." no q found -- error " cr | |
222 | 2drop \ XXX wrong | |
223 | 0 | |
224 | then | |
225 | ; | |
226 | ||
227 | \ token is the address of the endpoint for the interrupt. The endpoint is | |
228 | \ placed in one spot on the interrupt tree. | |
229 | \ toggle is the last one used. | |
230 | ||
231 | : disable-int-transactions ( token -- toggle ) | |
232 | dup skip-endp | |
233 | d# 10 ms \ XXX crude; wait for the distributor to have a chance | |
234 | \ to catch up with any transfers. Could check if | |
235 | \ any are outstanding via transfer-count. | |
236 | \ XXX needed if stuff happens on 10 ms tick timer level. | |
237 | dup dump-done-q | |
238 | dup ping-pong | |
239 | dup dump-done-q | |
240 | dup end-toggle swap | |
241 | dup wipe-endpoint | |
242 | dup interrupt-buf @ over caller-len @ give-chunk | |
243 | deque-interrupt | |
244 | ; | |
245 | ||
246 | headers | |
247 | ||
248 | \ Possible optimization: Re-use the transfer descriptor, since it is | |
249 | \ already allocated and partially ready for re-use. | |
250 | \ For now, brute force: dump transfer-d and get a new one. | |
251 | ||
252 | : re-arm-receive ( endp-d -- ) | |
253 | dup skip-endp next-frame \ make sure endpoint is not active | |
254 | \ >r r@ take-transfer-d's \ re-use transfer-d's | |
255 | dup dump-done-q | |
256 | dup ping-pong | |
257 | dup dump-done-q ( endp-d ) | |
258 | dup dup interrupt-buf @ over caller-len @ | |
259 | \ XXX use (add-data-transfer) to force a 0 len transfer?? | |
260 | rot 0 swap add-data-transfer \ dir is fake; take from endpoint | |
261 | dup use-endp-toggle | |
262 | unskip-endp | |
263 | ; | |
264 | ||
265 | : forward-data ( buf-addr endp-d -- ) | |
266 | sync-mem | |
267 | >r r@ interrupt-buf @ swap r> caller-len @ move | |
268 | ; | |
269 | ||
270 | \ Copy data from internal buffer to buf-addr | |
271 | : receive-int ( buf-addr token -- hw-err? | stat 0 ) | |
272 | dup done-waiting? if | |
273 | dup code-done-q @ dup condition-code if \ reply had error | |
274 | nip nip | |
275 | condition-code translate-code | |
276 | else \ reply ok | |
277 | drop | |
278 | dup re-arm-receive forward-data | |
279 | usb-ack 0 | |
280 | then | |
281 | else 2drop usb-nak 0 | |
282 | then | |
283 | ; | |
284 | ||
285 | \ XXX wrong; but we have no devices needing out interrupt transactions yet | |
286 | \ from memory to device. | |
287 | \ Copy data from buf-addr to internal buffer. UnSKIP the endpoint. | |
288 | : send-int ( buf-addr token -- hw-err? | stat 0 ) | |
289 | 2drop 2 0 | |
290 | ; | |
291 | ||
292 | : receive-int? ( token -- in? ) | |
293 | endpoint-control le-l@ | |
294 | h# 0000.1800 and | |
295 | h# 0000.1000 = | |
296 | ; | |
297 | ||
298 | external | |
299 | ||
300 | \ token is the address of the endpoint for the interrupt. The endpoint is | |
301 | \ placed in one spot on the interrupt tree. | |
302 | \ stat is ACK if interrupt fired, NACK if not, STALL if stalled. | |
303 | \ Re-arms the endpoint. Use its own buffers. Copy to/from the provided buffer | |
304 | \ when int-transaction-status is called. The client can copy out/in the | |
305 | \ provided buffer in between calls to int-transaction-status. | |
306 | \ Must be cognizant of dir in enable-int-transactions, in order to know | |
307 | \ which way to copy the data -- to or from the provided buffer. Must also | |
308 | \ unSKIP the endpoint if dir is out. | |
309 | ||
310 | : int-transaction-status ( buf-addr token -- hw-err? | stat 0 ) | |
311 | dup receive-int? if receive-int else send-int then | |
312 | ?.error | |
313 | ; | |
314 | ||
315 | ||
316 | \ OHCI bulk transfers use IN or OUT token stages. Sometimes can be followed | |
317 | \ directly by a status stage with no data. Otherwise data transfer stage. | |
318 | \ IN finishes with 0 len transfer (or no transfer if problem with data). | |
319 | \ OUT finishes with no extra transfer from host to target. The data toggles | |
320 | \ are synchronized via a separate control endpoint transaction. | |
321 | : execute-bulk ( toggle1 dir max-pkt buf-adr buf-len endpoint usb-addr | |
322 | -- toggle2 hw-err? | toggle2 stat 0 ) | |
323 | 2swap >r >r >r 0 -rot r> \ always full speed | |
324 | make-endpoint ( toggle dir endp-adr ) ( R: blen badr ) | |
325 | rot over set-endp-toggle | |
326 | tuck r> r> ( endp-adr dir endp-adr badr blen ) | |
327 | 2swap add-data-transfer | |
328 | dup use-endp-toggle ( endp-adr ) | |
329 | dup enque-bulk | |
330 | dup wait-for-last-reply | |
331 | over end-toggle -rot | |
332 | dup if over wipe-endpoint then | |
333 | swap deque-bulk ( toggle2 code ) | |
334 | translate-code | |
335 | ?.error | |
336 | ; | |
337 | ||
338 | ||
339 | : set-isoc-endpoint ( endp-d -- ) | |
340 | endpoint-control dup le-l@ | |
341 | h# 8000 or | |
342 | swap le-l! | |
343 | ; | |
344 | ||
345 | : set-isoc-direction ( dir endp-d -- ) | |
346 | endpoint-control tuck | |
347 | le-l@ h# 1800 not and | |
348 | swap if h# 800 else h# 1000 then or | |
349 | swap le-l! | |
350 | ; | |
351 | ||
352 | \ OHCI isoc transfers have IN or OUT token stages. Then data stage. | |
353 | \ No extra status stage. No data toggles. | |
354 | \ XXX not really correct, as the various packets can have their own error | |
355 | \ codes. | |
356 | \ XXX must be able to do 0 len transfers. Really need to be given a transfer | |
357 | \ schedule. probably should have both absolute and relative schedules. | |
358 | \ XXX new stack -- schedule added | |
359 | \ : execute-isochronous ( buf-adrn cntn ... buf-adr1 cnt1 n absolute? frame# | |
360 | \ dir max-pkt endpoint usb-addr -- hw-err? ) | |
361 | : execute-isochronous ( frame# dir max-pkt buf-adr buf-len endpoint usb-addr | |
362 | -- hw-err? ) | |
363 | \ XXX static allocation implies | |
364 | \ XXX need to copy the buffer to our buffer in order to get dev-addr | |
365 | 2swap >r >r >r 0 -rot r> \ always full speed | |
366 | make-endpoint ( frame# dir endp-d ) ( R: blen badr ) | |
367 | dup set-isoc-endpoint | |
368 | tuck set-isoc-direction ( f# endp-d ) ( R: blen badr ) | |
369 | r> r> ( f# endp-d badr blen ) | |
370 | rot dup >r ( f# badr blen endp-d ) ( R: endp-d ) | |
371 | add-isoc-transfer ( R: badr endp ) | |
372 | r@ enque-isoc | |
373 | r> dup wait-for-last-isoc-reply ( endp-d code ) | |
374 | swap deque-isoc ( code ) | |
375 | translate-code | |
376 | ?.error | |
377 | ; | |
378 | ||
379 | headers | |
380 | ||
381 | 2 value usb-address-counter | |
382 | ||
383 | external | |
384 | ||
385 | \ hub code needs to call up to get the next usb address to assign it | |
386 | \ to a device it finds when it executes its own probe stuff at power on. | |
387 | \ " next-usb-address" $call-parent. | |
388 | ||
389 | \ Only addresses 2 thru 127 are valid to be assigned. 1 is reserved for the | |
390 | \ root hub node. | |
391 | ||
392 | : next-usb-address ( -- n ) | |
393 | usb-address-counter d# 127 > if | |
394 | 0 | |
395 | else | |
396 | usb-address-counter | |
397 | dup 1+ is usb-address-counter | |
398 | then | |
399 | ; | |
400 | ||
401 | : current-frame ( -- n ) | |
402 | hcca frame# le-w@ | |
403 | ; | |
404 | ||
405 | headers | |
406 | ||
407 | \ Clear stall from endpoint via endpoint 0: | |
408 | : clear-stall ( speed endpoint usb-adr -- hw-err? | stat 0 ) | |
409 | >r | |
410 | request-blank >r ( R: usb-adr buf-adr ) | |
411 | clear-feature-req h# 200 or r@ request-type w! | |
412 | endpoint-stall r@ req-value le-w! | |
413 | r@ req-index le-w! ( speed ) ( R: usb-adr buf-adr ) | |
414 | 1 max-packet 0 0 | |
415 | r> /request | |
416 | 0 r> 2over >r >r \ goes to endpoint 0 | |
417 | execute-control | |
418 | r> r> give-chunk | |
419 | ; | |
420 | ||
421 | \ clean up errors: endpoint should be halted. all transfer-d's should | |
422 | \ be accounted for, either on the endpoint q or on one of its done-q's; | |
423 | \ the controller shouldn't be fiddling with any. they should really be | |
424 | \ on the endpoint q. dump all remaining transfer-d's. halt the control | |
425 | \ q, dump the endpoint, restart the control q. | |
426 | ||
427 | ||
428 | \ XXX Need to instancify the code. | |
429 | ||
430 | \ XXX Need to check the size of transfers to make sure it fits in one | |
431 | \ transfer-d, or else make more of them. |