summaryrefslogtreecommitdiff
path: root/roms/SLOF/slof/fs/usb/usb-support.fs
blob: c3c03656421542463e5139b14d9da19842f32a09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
\ *****************************************************************************
\ * 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 address 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 address 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 )
;