blob: aa85ab9cae3272cd3b7e7b58391403c9d6047ae3 [file] [log] [blame]
\ *****************************************************************************
\ * Copyright (c) 2004, 2008 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
s" allocate-ed failed!" usb-debug-print
4drop 2drop FALSE EXIT ( FALSE )
THEN ( dir addr dlen setup-packet MPS ep-fun ed-ptr )
TO temp1 ( dir addr dlen setup-packet MPS ep-fun )
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) ( ed-ptr setup-ptr td-head td-tail -- ed-ptr setup-ptr )
swap virt2phys swap virt2phys \ Convert td-head and td-tail to physical
3 pick ( ed-ptr s-ptr td-head' td-tail' ed-ptr )
tuck ( ed-ptr s-ptr td-head' ed-ptr td-tail' ed-ptr )
ed>tdqtp l!-le ( ed-ptr s-ptr td-head' ed-ptr )
ed>tdqhp l!-le ( ed-ptr s-ptr )
over ed>ned 0 swap l!-le ( ed-ptr s-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 phys2virt ( 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 swap virt2phys swap td>cbptr l!-le
( dir addr dlen ed-ptr setup-ptr td-head )
2dup td>bfrend swap STD-REQUEST-SETUP-SIZE 1- + virt2phys 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 phys2virt ( 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 phys2virt ( ed-ptr addr dlen dir tdqhp )
td>ntd l@-le phys2virt ( 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.
10 CONSTANT max-retire-td
: (transfer-wait-for-doneq) ( ed-ptr -- TRUE | FALSE )
dup virt2phys ( ed-ptr ed-ptr-dma )
hcctrhead rl!-le ( ed-ptr )
HC-enable-control-list-processing ( ed-ptr )
0 TO td-retire-count ( ed-ptr )
0 TO poll-timer ( ed-ptr )
BEGIN
td-retire-count num-tds <> ( ed-ptr TRUE | FALSE )
poll-timer max-retire-td < and ( ed-ptr TRUE | FALSE )
WHILE
(HC-CHECK-WDH) ( ed-ptr updated? )
IF
hchccadneq l@-le phys2virt
find-td-list-tail-and-size nip ( ed-ptr n )
td-retire-count + TO td-retire-count ( ed-ptr )
hchccadneq l@-le phys2virt dup ( ed-ptr done-td done-td )
(td-list-status) ( ed-ptr done-td failed-td CCcode )
IF
\ 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 ( ed-ptr done-td failed-td CCcode R: CCcode )
IF
max-retire-td 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 l!-le ( ed-ptr )
(HC-ACK-WDH) \ TDs were written to done queue. ACK the HC.
THEN
poll-timer 1+ TO poll-timer
4 ms \ longer 1 ms
REPEAT ( ed-ptr )
disable-control-list-processing ( ed-ptr )
td-retire-count num-tds <> ( ed-ptr )
IF
dup display-descriptors ( ed-ptr )
s" maximum of retire " usb-debug-print
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 there is 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 phys2virt 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 TRUE )
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 TRUE )
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 TRUE )
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 virt2phys 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 virt2phys hcbulkhead rl!-le
HC-enable-bulk-list-processing
ENDOF
2 OF \ Interrupt List.
2 TO saved-list-type
saved-rw-ed virt2phys hchccareg rl@-le phys2virt rl!-le
HC-enable-interrupt-list-processing
ENDOF
dup OF
saved-rw-ed ed>tdqhp l@-le phys2virt (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) ( TD-list TRUE|FALSE )
IF
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) ( td-list failed-TD CC )
IF
dup 4 =
IF
saved-list-type
CASE
0 OF
0 0 control-std-clear-feature
s" clear feature " usb-debug-print
ENDOF
1 OF \ clean bulk stalled
s" clear bulk when stalled " usb-debug-print
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
dup OF
s" unknown status " usb-debug-print
ENDOF
ENDCASE
ELSE ( td-list failed-TD CC )
." TD failed " 5b emit .s 5d emit cr
5040 error" (USB) device transaction error (wait-td-retire)."
ABORT
THEN
2drop drop
TRUE TO while-failed \ transaction failed
NEXT-TD 0<> \ clean the TD if we
IF
NEXT-TD (free-td-list) \ had a stalled
THEN
THEN
(free-td-list)
ELSE
drop \ drop td-list pointer
scan-time? IF 2e emit THEN \ show proceeding dots
TRUE TO while-failed
s" time out wait for done" usb-debug-print
20 ms \ wait for bad device
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
s" retired 1" usb-debug-print
ELSE
0
s" retired 0" usb-debug-print
THEN
\ s" retired " usb-debug-print-val
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 TRUE )
invert IF FALSE EXIT THEN
(td-prepare-rw) ( FALSE | pt ed-type toggle buffer length mps head TRUE )
invert IF FALSE EXIT THEN
(td-data-rw) ( FALSE | pt et head TRUE )
invert IF FALSE EXIT THEN
virt2phys saved-rw-ed ed>tdqhp l!-le ( pt et )
saved-rw-ed ed>tdqhp l@-le phys2virt
td>ntd l@-le phys2virt 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 limitation 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 )
;