diff options
Diffstat (limited to 'slof/fs/usb/usb-support.fs')
-rw-r--r-- | slof/fs/usb/usb-support.fs | 628 |
1 files changed, 628 insertions, 0 deletions
diff --git a/slof/fs/usb/usb-support.fs b/slof/fs/usb/usb-support.fs new file mode 100644 index 0000000..1326a04 --- /dev/null +++ b/slof/fs/usb/usb-support.fs @@ -0,0 +1,628 @@ +\ ***************************************************************************** +\ * Copyright (c) 2004, 2007 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +0 value NEXT-TD + +0 VALUE num-tds +0 VALUE td-retire-count +0 VALUE saved-tail +0 VALUE poll-timer +VARIABLE controlxfer-cmd + +\ Allocate an ED and populate it + +: (ed-prepare) ( dir addr dlen setup-packet MPS ep-fun -- + FALSE | dir addr dlen ed-ptr setup-ptr ) + allocate-ed dup 0= IF ( dir addr dlen setup-packet MPS ep-fun ed-ptr ) + drop 3drop 2drop FALSE EXIT ( FALSE ) + THEN + TO temp1 ( dir addr dlen setup-packet MPS ep-fun ) + \ s" controlxfer: Allocated ED: " temp1 usb-debug-print-val + temp1 zero-out-an-ed-except-link ( dir addr dlen setup-packet MPS ep-fun ) + temp1 ed>eattr l@-le or temp1 ed>eattr l!-le ( dir addr dlen setup-ptr MPS ) + dup TO temp2 10 lshift temp1 ed>eattr l@-le or temp1 ed>eattr l!-le + ( dir addr dlen setup-packet-address ) + temp1 swap TRUE ( dir addr dlen ed-ptr setup-ptr TRUE ) +; + + +\ Allocate TD list + + +: (td-prepare) ( dir addr dlen ed-ptr setup-ptr -- + dir FALSE | dir addr dlen ed-ptr setup-ptr td-head td-tail ) + 2 pick ( dir addr dlen ed-ptr setup-ptr dlen ) + temp2 ( dir addr dlen ed-ptr setup-ptr dlen MPS ) + /mod ( dir addr dlen ed-ptr setup-ptr rem quo ) + swap 0<> IF ( dir addr dlen ed-ptr setup-ptr quo ) + 1+ + THEN + 2+ + dup TO num-tds ( dir addr dlen ed-ptr setup-ptr quo+2 ) + allocate-td-list dup 0= IF ( dir addr dlen ed-ptr setup-ptr quo+2 ) + 2drop ( dir addr dlen ed-ptr setup-ptr ) + drop ( dir addr dlen ed-ptr ) + free-ed ( dir addr dlen ) + 2drop ( dir ) + FALSE ( dir FALSE ) + EXIT + THEN TRUE +; + + +\ Fill in the ED structure completely. + + +: (td-ready) ( dir addr dlen ed-ptr setup-ptr td-head td-tail -- ) + ( dir addr dlen ed-ptr setup-ptr ) + 3 pick ( dir addr dlen ed-ptr setup-ptr td-head td-tail ed-ptr ) + tuck ( dir addr dlen ed-ptr setup-ptr td-head ed-ptr td-tail ed-ptr ) + ed>tdqtp l!-le ( dir addr dlen ed-ptr setup-ptr td-head ed-ptr ) + ed>tdqhp l!-le ( dir addr dlen ed-ptr setup-ptr ) + over ed>ned 0 swap l!-le ( dir addr dlen ed-ptr setup-ptr ) +; + + +\ Initialize the HEAD and TAIL TDs for SETUP and +\ STATUS phase respectively. + + +: (td-setup-status) ( dir addr dlen ed-ptr setup-ptr -- dir addr dlen ed-ptr ) + over ed>tdqhp l@-le ( dir addr dlen ed-ptr setup-ptr td-head ) + dup zero-out-a-td-except-link ( dir addr dlen ed-ptr setup-ptr td-head ) + dup td>tattr DATA0-TOGGLE CC-FRESH-TD or swap l!-le + ( dir addr dlen ed-ptr setup-ptr td-head ) + 2dup td>cbptr l!-le ( dir addr dlen ed-ptr setup-ptr td-head ) + 2dup td>bfrend swap STD-REQUEST-SETUP-SIZE 1- + swap l!-le + ( dir addr dlen ed-ptr setup-ptr td-head ) + 2drop ( dir addr dlen ed-ptr ) +; + +\ Initialize the TD TAIL pointer. + + +: (td-tailpointer) ( dir addr dlen ed-ptr -- dir addr dlen ed-ptr ) + dup ed>tdqtp l@-le ( dir addr dlen ed-ptr td-tail ) + dup zero-out-a-td-except-link ( dir addr dlen ed-ptr td-tail ) + dup td>tattr dup l@-le DATA1-TOGGLE CC-FRESH-TD or or swap l!-le + ( dir addr dlen ed-ptr td-tail ) + 4 pick 0= ( dir addr dlen ed-ptr td-tail flag ) + 3 pick 0<> ( dir addr dlen ed-ptr td-tail flag flag ) + and IF ( dir addr dlen ed-ptr td-tail ) + dup td>tattr dup l@-le TD-DP-OUT or swap l!-le + ( dir addr dlen ed-ptr td-tail ) + ELSE + dup td>tattr dup l@-le TD-DP-IN or swap l!-le + ( dir addr dlen ed-ptr td-tail ) + THEN + drop ( dir addr dlen ed-ptr ) +; + +\ Initialize the Data TDs. + + +: (td-data) ( dir addr dlen ed-ptr -- ed-ptr ) + -rot ( dir ed-ptr addr dlen ) + dup 0<> IF ( dir ed-ptr addr dlen ) + >r >r >r TO temp1 r> r> r> temp1 ( ed-ptr addr dlen dir ) + 3 pick ( ed-ptr addr dlen dir ed-ptr ) + ed>tdqhp l@-le td>ntd l@-le ( ed-ptr addr dlen dir td-datahead ) + 4 pick ( ed-ptr addr dlen dir td-datahead ed-ptr ) + td>tattr l@-le 10 rshift ( ed-ptr addr dlen dir td-head-data MPS ) + swap ( ed-ptr addr dlen dir MPS td-head-data ) + >r >r >r >r >r 1 r> r> r> r> r> + ( ed-ptr 1 addr dlen dir MPS td-head-data ) + >r >r 0= IF ( ed-ptr 1 addr dlen dir ) + OHCI-DP-IN ( ed-ptr 1 addr dlen dir OHCI-DP-IN ) + ELSE + OHCI-DP-OUT ( ed-ptr 1 addr dlen dir OHCI-DP-OUT ) + THEN + r> r> ( ed-ptr 1 addr dlen dir OHCI-DP- MPS td-head-data ) + fill-TD-list + ELSE + 2drop nip ( ed-ptr ) + THEN +; + + +\ Program the HC with the ed-ptr value and wait for status to +\ from the HC. +\ Free the ED and TDs associated with it. +\ PENDING: Above said. + + +: (transfer-wait-for-doneq) ( ed-ptr -- TRUE | FALSE ) + dup ( ed-ptr ed-ptr ) + hcctrhead rl!-le ( ed-ptr ) + HC-enable-control-list-processing ( ed-ptr ) + 0 TO td-retire-count ( ed-ptr ) + 0 TO poll-timer BEGIN ( ed-ptr ) + td-retire-count num-tds <> ( ed-ptr TRUE | FALSE ) + poll-timer d# 5000 < and ( ed-ptr TRUE | FALSE ) + WHILE + (HC-CHECK-WDH) IF ( ed-ptr ) + hchccadneq rl@-le find-td-list-tail-and-size nip ( ed-ptr n ) + td-retire-count + TO td-retire-count ( ed-ptr ) + hchccadneq rl@-le dup ( ed-ptr done-td done-td ) + (td-list-status) IF ( ed-ptr done-td failed-td CCcode ) + \ keep condition code of TD on return stack + dup >r + s" (transfer-wait-for-doneq: USB device communication error." + usb-debug-print ( ed-ptr done-td failed-td CCcode R: CCcode ) + dup 4 = swap dup 5 = rot or IF ( ed-ptr done-td failed-td CCcode R: CCcode ) + d# 5000 TO poll-timer ( ed-ptr done-td failed-td CCcode R: CCcode ) + THEN + ( ed-ptr done-td failed-td CCcode R: CCcode) + usb-debug-flag IF + s" CC code ->" type . cr + s" Failing TD contents:" type cr display-td + ELSE + 2drop + THEN ( ed-ptr done-td R: CCcode) + controlxfer-cmd @ GET-MAX-LUN = r> 4 = and IF + s" (transfer-wait-for-doneq): GET-MAX-LUN ControlXfer STALLed" + usb-debug-print + \ Condition Code = 4 means that the device does not support multiple LUNS + \ see USB Massbulk 1.0 Standard + ELSE + drop + 5030 error" (USB) Device communication error." + ABORT + \ FIXME: ABORTing here might leave the HC in an unusable state. + \ We should maybe rather ABORT at the end of this Forth + \ word, when clean-up has been done (or not ABORT at all) + THEN + THEN ( ed-ptr done-td ) + (free-td-list) ( ed-ptr ) + 0 hchccadneq rl!-le ( ed-ptr ) + (HC-ACK-WDH) \ TDs were written to DOne queue. ACK the HC. + \ s" Retired = " td-retire-count usb-debug-print-val + \ s" Total = " num-tds usb-debug-print-val + THEN + poll-timer 1+ TO poll-timer + 1 ms + REPEAT ( ed-ptr ) + disable-control-list-processing ( ed-ptr ) + td-retire-count num-tds <> IF ( ed-ptr ) + dup display-descriptors ( ed-ptr ) + THEN + free-ed + td-retire-count num-tds <> IF + FALSE ( FALSE ) + ELSE + TRUE ( TRUE ) + THEN +; + + +\ COLON DEFINITION: controlxfer +\ INTERFACE FUNCTION + +\ ARGUMENTS: +\ (from the bottom OF stack) +\ 1. dir -- This is the direction OF data transfer associated with +\ the DATA STAGE OF the control xfer. +\ If there is no data transfer (argument dlen is zero) +\ THEN this argument DOes not matter, nonethless it has +\ to be passed. +\ A "0" represents an IN and "1" represents an "OUT". +\ 2. addr -- If therez a data stage associated with the transfer, +\ THEN, this argument holds the address OF the data buffer +\ 3. dlen -- This arg holds the length OF the data buffer discussed +\ in previous step (addr) +\ 4. setup-packet -- This holds the pointer to the setup packet that +\ will be transmitted during the SETUP stage OF +\ the control xfer. The function assumes the length +\ OF the status packet to be 8 bytes. +\ 5. MPS -- This is the MAX PACKET SIZE OF the endpoint. +\ 6. ep-fun -- This is the 11-bit value that holds the Endpoint and +\ the function address. bit 7 to bit 10 holds the Endpoint +\ address. Bits 0 to Bit 6 holds the Function Address. +\ The BIT numbering followed : The left most bit is referred +\ as bit 0. (not the one followed by PPC) +\ Bit 13 must be set for low-speed devices. + +\ RETURN VALUE: +\ Returns TRUE | FALSE depending on the success OF the transaction. + +\ ASSUMPTIONS: +\ 1. Function assumes that the setup packet is 8-bytes in length. +\ If in future, IF we need to add a new argument, we need to change +\ the function in lot OF places. + +\ RISKS: +\ 1. If for some reason, the USB controller DOes not retire all the TDs +\ THEN, the status checking part OF this "word" can spin forever. + + +: controlxfer ( dir addr dlen setup-packet MPS ep-fun -- TRUE | FALSE ) + 2 pick @ controlxfer-cmd ! + (ed-prepare) ( FALSE | dir addr dlen ed-ptr setup-ptr ) + invert IF FALSE EXIT THEN + (td-prepare) ( pt ed-type toggle buffer length mps head ) + invert IF FALSE EXIT THEN + (td-ready) ( dir addr dlen ed-ptr setup-ptr ) + (td-setup-status) ( dir addr dlen ed-ptr ) + (td-tailpointer) ( dir addr dlen ed-ptr ) + (td-data) ( ed-ptr ) + + + \ FIXME: + \ Clear the TAIL pointer in ED. This has got sthg to DO with how + \ the HC finds an EMPTY queue condition. Refer spec. + + + dup ed>tdqtp l@-le TO saved-tail ( ed-ptr ) + dup ed>tdqtp 0 swap l!-le ( ed-ptr ) + (transfer-wait-for-doneq) ( TRUE | FALSE ) +; + +0201000000000000 CONSTANT CLEARHALTFEATURE +0 VALUE endpt-num +0 VALUE usb-addr-contr-req +: control-std-clear-feature ( endpoint-nr usb-addr -- TRUE|FALSE ) + TO usb-addr-contr-req \ usb address + TO endpt-num \ endpoint number + CLEARHALTFEATURE setup-packet ! + endpt-num setup-packet 4 + c! \ endpoint number + 0 0 0 setup-packet DEFAULT-CONTROL-MPS usb-addr-contr-req controlxfer + ( TRUE|FALSE ) +; + +\ It resets the usb bulk-device +21FF000000000000 CONSTANT BULK-RESET +: control-std-bulk-reset ( usb-addr -- TRUE|FALSE ) + TO usb-addr-contr-req + BULK-RESET setup-packet ! + 0 0 0 setup-packet DEFAULT-CONTROL-MPS usb-addr-contr-req controlxfer + ( TRUE|FALSE ) +; + +: bulk-reset-recovery-procedure ( bulk-out-endp bulk-in-endp usb-addr -- ) + >r ( bulk-out-endp bulk-in-endp R: usb-addr ) + \ perform a bulk reset + r@ control-std-bulk-reset + IF s" bulk reset OK" + ELSE s" bulk reset failed" + THEN usb-debug-print + + \ clear bulk-in endpoint ( bulk-out-endp bulk-in-endp R: usb-addr ) + 80 or r@ control-std-clear-feature + IF s" control-std-clear IN endpoint OK" + ELSE s" control-std-clear-IN endpoint failed" + THEN usb-debug-print + + \ clear bulk-out endpoint ( bulk-out-endp R: usb-addr ) + r@ control-std-clear-feature + IF s" control-std-clear OUT endpoint OK" + ELSE s" control-std-clear-OUT endpoint failed" + THEN usb-debug-print + r> drop +; + +0 VALUE saved-rw-ed +0 VALUE num-rw-tds +0 VALUE num-rw-retired-tds +0 VALUE saved-rw-start-toggle +0 VALUE saved-list-type + +\ Allocate an ED and populate what you can. + + +: (ed-prepare-rw) + ( pt ed-type toggle buffer length mps address ed-ptr -- + FALSE | pt ed-type toggle buffer length mps ) + allocate-ed dup 0= IF + ( pt ed-type toggle buffer length mps address ed-ptr ) + drop 2drop 2drop 2drop drop + saved-rw-start-toggle FALSE EXIT ( toggle FALSE ) + THEN + TO saved-rw-ed ( pt ed-type toggle buffer length mps address ) + saved-rw-ed zero-out-an-ed-except-link + ( pt ed-type toggle buffer length mps address ) + saved-rw-ed ed>eattr l!-le ( pt ed-type toggle buffer length mps ) + dup 10 lshift saved-rw-ed ed>eattr l@-le or + ( pt ed-type toggle buffer length mps mps~ ) + saved-rw-ed ed>eattr l!-le TRUE ( pt ed-type toggle buffer length mps TRUE ) +; + + +\ Allocate TD List + + +: (td-prepare-rw) + ( pt ed-type toggle buffer length mps -- + FALSE | pt ed-type toggle buffer length mps head ) + 2dup ( pt ed-type toggle buffer length mps length mps ) + /mod ( pt ed-type toggle buffer length mps num-tds rem ) + swap 0<> IF ( pt ed-type toggle buffer length mps num-tds ) + 1+ ( pt ed-type toggle buffer length mps num-tds+1 ) + THEN + dup TO num-rw-tds ( pt ed-type toggle buffer length mps num-tds ) + allocate-td-list ( pt ed-type toggle buffer length mps head tail ) + dup 0= IF + 2drop 2drop 2drop 2drop + saved-rw-ed free-ed + ." rw-endpoint: TD list allocation failed" cr + saved-rw-start-toggle FALSE ( FALSE ) + EXIT + THEN + drop TRUE ( pt ed-type toggle buffer length mps head TRUE ) +; + + +\ Populate TD list with data buffers and toggle info. + + +: (td-data-rw) + ( pt ed-type toggle buffer length mps head -- FALSE | pt et head ) + 6 pick ( pt ed-type toggle buffer length mps head pt ) + FALSE TO case-failed CASE + 0 OF OHCI-DP-IN ENDOF + 1 OF OHCI-DP-OUT ENDOF + 2 OF OHCI-DP-SETUP ENDOF + dup OF TRUE TO case-failed + ." rw-endpoint: Invalid Packet Type!" cr + ENDOF + ENDCASE ( pt ed-type toggle buffer length mps head dp ) + case-failed IF + saved-rw-ed free-ed ( pt ed-type toggle buffer length mps head dp ) + drop (free-td-list) ( pt ed-type toggle buffer length mps head ) + 2drop 2drop 2drop + saved-rw-start-toggle FALSE ( FALSE ) + EXIT ( FALSE ) + THEN + -rot ( pt ed-type toggle buffer length dp mps head ) + dup >r ( pt ed-type toggle buffer length dp mps head ) + fill-TD-list r> TRUE ( pt et head TRUE ) +; + + +\ Enqueue the ED with the appropriate list + + +: (ed-ready-rw) ( pt et -- - | toggle FALSE ) + nip ( et ) + FALSE TO case-failed CASE + 0 OF \ Control List. Queue the ED to control list + 0 TO saved-list-type + saved-rw-ed hcctrhead rl!-le + HC-enable-control-list-processing + ENDOF + 1 OF \ Bulk List. Queue the ED to bulk list + 1 TO saved-list-type + saved-rw-ed hcbulkhead rl!-le + HC-enable-bulk-list-processing + ENDOF + 2 OF \ Interrupt List. + 2 TO saved-list-type + saved-rw-ed hchccareg rl@-le rl!-le + HC-enable-interrupt-list-processing + ENDOF + dup OF + saved-rw-ed ed>tdqhp l@-le (free-td-list) + saved-rw-ed free-ed + TRUE TO case-failed + ENDOF + ENDCASE + case-failed IF + saved-rw-start-toggle FALSE ( toggle FALSE ) + EXIT + THEN + TRUE ( TRUE ) +; + +\ Wait for TDs to return to the Done Q. + +: (wait-td-retire) ( -- ) + 0 TO num-rw-retired-tds + FALSE TO while-failed BEGIN + num-rw-retired-tds num-rw-tds < ( TRUE | FALSE ) + while-failed FALSE = and ( TRUE | FALSE ) + WHILE + d# 5000 (wait-for-DOne-q) IF ( TD-list ) + dup find-td-list-tail-and-size nip ( td-list size ) + num-rw-retired-tds + TO num-rw-retired-tds ( td-list ) + dup (td-list-status) IF ( td-list failed-TD CC ) + dup 4 = IF + saved-list-type CASE + 0 OF 0 0 control-std-clear-feature + ENDOF + 1 OF \ clean bulk stalled + disable-bulk-list-processing \ disable procesing + saved-rw-ed ed>eattr l@-le dup \ extract + 780 and 7 rshift 80 or \ endpoint and + swap 7f and \ usb addr + control-std-clear-feature + ENDOF + 2 OF 0 saved-rw-ed ed>eattr l@-le + control-std-clear-feature ENDOF + ENDCASE + ELSE + usb-debug-flag IF + s" TD failed with CC code: " type . cr + THEN + drop drop + \ TRUE ABORT" USB device transaction error." + 5040 error" (USB) device transaction error." + ABORT + THEN + 2drop drop + TRUE TO while-failed \ transaction failed + NEXT-TD 0<> IF \ clean the TD if we + NEXT-TD (free-td-list) \ had a stalled + THEN + THEN + (free-td-list) + ELSE + drop \ drop td-list pointer + TRUE TO while-failed + THEN + REPEAT +; + + +\ Process retired TDs + + +: (process-retired-td) ( -- TRUE | FALSE ) + saved-list-type CASE + 0 OF disable-control-list-processing ENDOF + 1 OF disable-bulk-list-processing ENDOF + 2 OF disable-interrupt-list-processing ENDOF + ENDCASE + saved-rw-ed ed>tdqhp l@-le 2 and 0<> IF + 1 + ELSE + 0 + THEN + WHILE-failed IF + FALSE ( FALSE ) + ELSE + TRUE ( TRUE ) + THEN + saved-rw-ed free-ed +; + + +\ (DO-rw-endpoint): T1 12 80 0 0chis method is an privately visible function +\ to be used by the "rw-endpoint" the required +\ number OF times based on the actual length +\ to be transferred + +\ Arguments: +\ pt: Packet type +\ 0 -> IN +\ 1 -> OUT +\ 2 -> SETUP +\ et: Endpoint type +\ 0 -> Control +\ 1 -> Bulk +\ toggle: Starting toggle for this transfer +\ buffer length: Data buffer associated with the transfer limited +\ accordingly by the "rw-endpoint" method to the +\ value OF max packet size +\ mps: Max Packet Size. +\ address: Address OF endpoint. 11-bit address. The lower 7-bits represent +\ the USB addres and the upper 4-bits represent the Endpoint +\ number. + + + +: (do-rw-endpoint) + ( pt ed-type toggle buffer length mps address -- toggle TRUE|toggle FALSE ) + 4 pick ( pt ed-type toggle buffer length mps address toggle ) + TO saved-rw-start-toggle ( pt ed-type toggle buffer length mps address) + (ed-prepare-rw) ( FALSE | pt ed-type toggle buffer length mps ) + invert IF FALSE EXIT THEN + (td-prepare-rw) ( FALSE | pt ed-type toggle buffer length mps head ) + invert IF FALSE EXIT THEN + (td-data-rw) ( FALSE | pt et head ) + invert IF FALSE EXIT THEN + saved-rw-ed ed>tdqhp l!-le ( pt et ) + saved-rw-ed ed>tdqhp l@-le td>ntd l@-le TO NEXT-TD \ save for a stalled + (ed-ready-rw) + invert IF FALSE EXIT THEN + (wait-td-retire) + (process-retired-td) ( TRUE | FALSE ) +; + + +\ rw-endpoint: The method is an externally visible method to be exported +\ to the child nodes. It uses the internal method +\ "(DO-rw-endpoint)", the required number OF times based on the +\ actual length OF transfer, so that the limitataion OF MAX-TDS +\ DO not hinder the transfer. + +\ Arguments: +\ pt: Packet type +\ 0 -> IN +\ 1 -> OUT +\ 2 -> SETUP +\ et: Endpoint type +\ 0 -> Control +\ 1 -> Bulk +\ toggle: Starting toggle for this transfer +\ buffer length: Data buffer associated with the transfer +\ mps: Max Packet Size. +\ address: Address OF endpoint. 11-bit address. The lower 7-bits represent +\ the USB addres and the upper 4-bits represent the Endpoint +\ number. + + +0 VALUE transfer-len +0 VALUE mps-current +0 VALUE addr-current +0 VALUE usb-addr +0 VALUE toggle-current +0 VALUE type-current +0 VALUE pt-current +0 VALUE read-status +0 VALUE counter +0 VALUE residue + + +: rw-endpoint + ( pt ed-type toggle buffer length mps address -- ) + ( toggle TRUE |toggle FALSE ) + + \ a single transfer descriptor can point to a buffer OF + \ 8192 bytes a block on the CDROM has 2048 bytes + \ but a single transfer is constrained by the MPS + + 2 pick TO transfer-len ( pt ed-type toggle buffer length mps address ) + 1 pick TO mps-current ( pt ed-type toggle buffer length mps address ) + TRUE TO read-status ( pt ed-type toggle buffer length mps address ) + transfer-len mps-current num-free-tds * <= IF + (do-rw-endpoint) ( toggle TRUE | toggle FALSE ) + TO read-status ( toggle ) + TO toggle-current + ELSE + TO usb-addr ( pt ed-type toggle buffer length mps ) + 2drop ( pt ed-type toggle buffer ) + TO addr-current ( pt ed-type toggle ) + TO toggle-current ( pt ed-type ) + TO type-current ( pt ) + TO pt-current + transfer-len mps-current num-free-tds * /mod ( residue count ) + ( remainder=residue quotient=count ) + TO counter ( residue ) + TO residue + mps-current num-free-tds * TO transfer-len BEGIN + counter 0 > ( TRUE | FALSE ) + read-status TRUE = and ( TRUE | FALSE ) + WHILE + pt-current type-current toggle-current ( pt ed-type toggle ) + addr-current transfer-len ( pt ed-type toggle buffer length ) + mps-current ( pt ed-type toggle buffer length mps ) + usb-addr (do-rw-endpoint) ( toggle TRUE | toggle FALSE ) + TO read-status ( toggle ) + TO toggle-current + addr-current transfer-len + TO addr-current + counter 1- TO counter + REPEAT + residue 0<> ( TRUE |FALSE ) + read-status TRUE = and IF + residue TO transfer-len + pt-current type-current toggle-current ( pt ed-type toggle ) + addr-current transfer-len ( pt ed-type toggle buffer length ) + mps-current ( pt ed-type toggle buffer length mps ) + usb-addr (do-rw-endpoint) ( toggle TRUE | toggle FALSE ) + TO read-status + TO toggle-current + THEN + THEN + read-status invert IF + THEN + toggle-current ( toggle ) + read-status ( TRUE | FALSE ) +; |