|
|
1.1 root 1: \ *****************************************************************************
2: \ * Copyright (c) 2004, 2008 IBM Corporation
3: \ * All rights reserved.
4: \ * This program and the accompanying materials
5: \ * are made available under the terms of the BSD License
6: \ * which accompanies this distribution, and is available at
7: \ * http://www.opensource.org/licenses/bsd-license.php
8: \ *
9: \ * Contributors:
10: \ * IBM Corporation - initial implementation
11: \ ****************************************************************************/
12:
13:
14: \ We expect to base address of the OHCI controller on the stack:
15:
16: CONSTANT baseaddrs
17:
18: s" OHCI base address = " baseaddrs usb-debug-print-val
19:
20:
21: \ Open Firmware Properties
22:
23:
24: s" usb" 2dup device-name device-type
25: 1 encode-int s" #address-cells" property
26: 0 encode-int s" #size-cells" property
27:
28:
29: \ converts physical address to text unit string
30:
31:
32: : encode-unit ( port -- unit-str unit-len ) 1 hex-encode-unit ;
33:
34:
35: \ Converts text unit string to phyical address
36:
37:
38: : decode-unit ( addr len -- port ) 1 hex-decode-unit ;
39:
40:
41: \ Data Structure Definitions
42: \ OHCI Task Descriptor Structure.
43:
44:
45: STRUCT
46: /l field td>tattr
47: /l field td>cbptr
48: /l field td>ntd
49: /l field td>bfrend
50: CONSTANT /tdlen
51:
52:
53: \ OHCI Endpoint Descriptor Structure.
54:
55:
56: STRUCT
57: /l field ed>eattr
58: /l field ed>tdqtp
59: /l field ed>tdqhp
60: /l field ed>ned
61: CONSTANT /edlen
62:
63:
64: \ HCCA Done queue location packaged as a structure for ease OF use.
65:
66:
67: STRUCT
68: /l field hc>hcattr
69: /l field hc>hcdone
70: CONSTANT /hclen
71:
72:
73: \ OHCI Memory Mapped Registers
74:
75:
76: \ : get-base-address ( -- baseaddr )
77: \ s" assigned-addresses" get-my-property IF
78: \ s" not possible" usb-debug-print
79: \ -1
80: \ ELSE ( addr len )
81: \ decode-int drop ( addr len )
82: \ decode-int drop ( addr len )
83: \ decode-int nip nip ( n )
84: \ THEN
85: \ \ TODO: Use translate-address here
86: \ ;
87:
88: \ get-base-address CONSTANT baseaddrs
89:
90: baseaddrs CONSTANT HcRevision
91: baseaddrs 4 + CONSTANT hccontrol
92: baseaddrs 8 + CONSTANT hccomstat
93: baseaddrs 0c + CONSTANT hcintstat
94: baseaddrs 14 + CONSTANT hcintdsbl
95: baseaddrs 18 + CONSTANT hchccareg
96: baseaddrs 20 + CONSTANT hcctrhead
97: baseaddrs 24 + CONSTANT hccurcont
98: baseaddrs 28 + CONSTANT hcbulkhead
99: baseaddrs 2c + CONSTANT hccurbulk
100: baseaddrs 30 + CONSTANT hcdnehead
101: baseaddrs 34 + CONSTANT hcintrval
102: baseaddrs 40 + CONSTANT HcPeriodicStart
103: baseaddrs 48 + CONSTANT hcrhdescA
104: baseaddrs 4c + CONSTANT hcrhdescB
105: baseaddrs 50 + CONSTANT HcRhStatus
106: baseaddrs 54 + CONSTANT hcrhpstat
107: baseaddrs 58 + CONSTANT hcrhpstat2
108: baseaddrs 5c + CONSTANT hcrhpstat3
109:
110: usb-debug-flag IF
111: 0 config-l@ ." - VENDOR: " 8 .r cr
112: 40 config-l@ ." - PMC : " 8 .r
113: 44 config-l@ ." PMCSR : " 8 .r cr
114: E0 config-l@ ." - EXT1 : " 8 .r
115: E4 config-l@ ." EXT2 : " 8 .r cr
116: THEN
117:
118: \ Constants for INTSTAT register
119:
120: 2 CONSTANT WDH
121:
122: \ Constants for RH Port Status Register
123:
124: 1 CONSTANT RHP-CCS \ Current Connect Status
125: 2 CONSTANT RHP-PES \ Port Enable Status
126: 10 CONSTANT RHP-PRS \ Port Reset Status
127: 100 CONSTANT RHP-PPS \ Port Power Status
128: 10000 CONSTANT RHP-CSC \ Connect Status Changed
129: 100000 CONSTANT RHP-PRSC \ Port Reset Status Changed
130:
131:
132: \ Constants for OHCI
133:
134: 0 CONSTANT OHCI-DP-SETUP
135: 1 CONSTANT OHCI-DP-OUT
136: 2 CONSTANT OHCI-DP-IN
137: 3 CONSTANT OHCI-DP-INVALID
138:
139: \ 8-byte Standard Device Requests + Hub class specific requests.
140:
141: 8006000100001200 CONSTANT get-ddescp
142: 8006000200000900 CONSTANT get-cdescp
143: 8006000400000900 CONSTANT get-idescp
144: 8006000500000700 CONSTANT get-edescp
145: A006000000001000 CONSTANT get-hdescp
146: 0009010000000000 CONSTANT set-cdescp
147: 2303010004000000 CONSTANT hpenable-set
148: 2303040001000000 CONSTANT hp1rst-set
149: 2303040002000000 CONSTANT hp2rst-set
150: 2303040003000000 CONSTANT hp3rst-set
151: 2303040004000000 CONSTANT hp4rst-set
152: 2303080001000000 CONSTANT hp1pwr-set
153: 2303080002000000 CONSTANT hp2pwr-set
154: 2303080003000000 CONSTANT hp3pwr-set
155: 2303080004000000 CONSTANT hp4pwr-set
156: A003000000000400 CONSTANT hstatus-get
157: A300000001000400 CONSTANT hp1sta-get
158: A300000002000400 CONSTANT hp2sta-get
159: A300000003000400 CONSTANT hp3sta-get
160: A300000004000400 CONSTANT hp4sta-get
161: 8008000000000100 CONSTANT get-config
162:
163: A1FE000000000100 CONSTANT GET-MAX-LUN
164:
165: 2 18 lshift CONSTANT DATA0-TOGGLE
166: 3 18 lshift CONSTANT DATA1-TOGGLE
167: 0f 1c lshift CONSTANT CC-FRESH-TD
168: 8 CONSTANT STD-REQUEST-SETUP-SIZE
169: 0 13 lshift CONSTANT TD-DP-SETUP
170: 1 13 lshift CONSTANT TD-DP-OUT
171: 2 13 lshift CONSTANT TD-DP-IN
172:
173: 400001 CONSTANT ed-cntatr
174: 400002 CONSTANT ed-cntatr1
175: 80081 CONSTANT ed-hubatr
176: 80000 CONSTANT ed-defatr
177: 0f0e40000 CONSTANT td-attr
178: 00 VALUE ptr
179:
180:
181: \ TD Management constants and Data structures.
182:
183:
184: 200 CONSTANT MAX-TDS
185: 0 VALUE td-freelist-head
186: 0 VALUE td-freelist-tail
187: 0 VALUE num-free-tds
188: 0 VALUE max-rh-ports
189: 0 VALUE current-stat
190:
191: INSTANCE VARIABLE td-list-region
192:
193: \ ED Management constants
194:
195:
196: 14 CONSTANT MAX-EDS
197: 0 VALUE ed-freelist-head
198: 0 VALUE num-free-eds
199: INSTANCE VARIABLE ed-list-region
200: 0 VALUE usb-address
201: 0 VALUE initial-hub-address
202: 0 VALUE new-device-address
203: 0 VALUE mps
204: 0 VALUE DEBUG-TDS
205: 0 VALUE case-failed \ available for general use to see IF a CASE statement
206: \ failed or not.
207: 0 VALUE WHILE-failed \ available for general use to see IF a WHILE LOOP
208: \ failed in the middle. Used to break from the
209: \ WHILE LOOP
210:
211: 8 CONSTANT DEFAULT-CONTROL-MPS
212: 12 CONSTANT DEVICE-DESCRIPTOR-LEN
213: 1 CONSTANT DEVICE-DESCRIPTOR-TYPE
214: 1 CONSTANT DEVICE-DESCRIPTOR-TYPE-OFFSET
215: 4 CONSTANT DEVICE-DESCRIPTOR-DEVCLASS-OFFSET
216: 7 CONSTANT DEVICE-DESCRIPTOR-MPS-OFFSET
217:
218: 20 CONSTANT BULK-CONFIG-DESCRIPTOR-LEN
219:
220: 9 CONSTANT HUB-DEVICE-CLASS
221: 0 CONSTANT NO-CLASS
222:
223: VARIABLE setup-packet \ 8 bytes for setup packet
224: VARIABLE ch-buffer \ 1 byte character buffer
225:
226: INSTANCE VARIABLE dd-buffer
227: INSTANCE VARIABLE cd-buffer
228:
229:
230: \ Temporary variables for functions. These variables have to be initialized
231: \ before usage in functions and their values assume significance only during
232: \ the function's execution time. Should be used like local variables.
233: \ CAUTION:
234: \ If you are calling functions that destroy contents OF these variables, be
235: \ smart enuf to save the values before calling them.
236: \ It is recommended that these temporary variables are used only amidst normal
237: \ FORTH words -- not among the vicinity OF any OF the functions OF this node.
238:
239:
240: 0 VALUE temp1
241: 0 VALUE temp2
242: 0 VALUE temp3
243: 0 VALUE extra-bytes
244: 0 VALUE num-td
245: 0 VALUE current
246:
247: 0 VALUE device-speed
248:
249:
250: \ Debug functions for displaying ED, TD and their combo list.
251:
252: : Show-OHCI-Register
253: ." -> OHCI-Register: " cr
254: ." - HcControl : " hccontrol rl@-le 8 .r
255: ." CmdStat : " hccomstat rl@-le 8 .r
256: ." HcInterr. : " hcintstat rl@-le 8 .r cr
257:
258: ." - HcFmIntval: " hcintrval rl@-le 8 .r
259: ." Per. Start: " HcPeriodicStart rl@-le 8 .r cr
260:
261: ." - PortStat-1: " hcrhpstat rl@-le 8 .r
262: ." PortStat-2: " hcrhpstat2 rl@-le 8 .r
263: ." PortStat-3: " hcrhpstat3 rl@-le 8 .r cr
264:
265: ." Descr-A : " hcrhdescA rl@-le 8 .r
266: ." Descr-B : " hcrhdescB rl@-le 8 .r
267: ." HcRhStat : " HcRhStatus rl@-le 8 .r cr
268: ;
269:
270: : display-ed ( ED-ADDRESS -- )
271: TO temp1
272: usb-debug-flag IF
273: s" Dump OF ED " type temp1 u. cr
274: s" eattr : " type temp1 ed>eattr l@-le u. cr
275: s" tdqhp : " type temp1 ed>tdqhp l@-le u. cr
276: s" tdqtp : " type temp1 ed>tdqtp l@-le u. cr
277: s" ned : " type temp1 ed>ned l@-le u. cr
278: THEN
279: ;
280:
281:
282: \ Displays the transfer descriptors
283:
284: : display-td ( TD-ADDRESS -- )
285: TO temp1
286: usb-debug-flag IF
287: s" TD " type temp1 u. s" dump: " type cr
288: s" td>tattr : " type temp1 td>tattr l@-le u. cr
289: s" td>cbptr : " type temp1 td>cbptr l@-le u. cr
290: s" td>ntd : " type temp1 td>ntd l@-le u. cr
291: s" td>bfrend : " type temp1 td>bfrend l@-le u. cr
292: THEN
293: ;
294:
295:
296: \ display's the descriptors
297:
298:
299: : display-descriptors ( ED-ADDRESS -- )
300: 10 1- not and ( ED-ADDRESS~ )
301: dup display-ed ed>tdqhp l@-le BEGIN ( ED-ADDRESS~ )
302: 10 1- not and ( ED-ADDRESS~ )
303: dup 0<> ( ED-ADDRESS~ TRUE | FALSE )
304: WHILE
305: dup display-td td>ntd l@-le ( ED-ADDRESS~ )
306: REPEAT
307: drop
308: ;
309:
310:
311: \ ---------------------------------------------------------------------------
312: \ TD LIST MANAGEMENT WORDS
313: \ ------------------------
314: \ The following are WORDS internal to this node. They are supposed to
315: \ be used by other WORDS inside this device node.
316: \ The first three WORDS below form the interface. The fourth and fifth
317: \ word is a helper function and is not exposed to other portions OF this
318: \ device node.
319: \ a) initialize-td-free-list
320: \ b) allocate-td-list
321: \ c) (free-td-list)
322: \ d) find-td-list-tail-and-size
323: \ e) zero-out-a-td-except-link
324: \ ----------------------------------------------------------------------------
325:
326:
327: : zero-out-a-td-except-link ( td -- )
328:
329:
330: \ There r definitely smarter ways to DO it especially
331: \ on a 64-bit machine.
332:
333: \ Optimization, Portability:
334: \ --------------------------
335: \ Replace the following code by two "!" OF zeroes. Since
336: \ we know that an "td" is actually 16 bytes and that we
337: \ will be executing on a 64-bit machine, we can finish OFf
338: \ with 2 stores. But that WONT be portable.
339:
340:
341: dup 0 swap td>tattr l!-le ( td )
342: dup 0 swap td>cbptr l!-le ( td )
343: dup 0 swap td>bfrend l!-le ( td )
344: drop
345: ;
346:
347:
348: \ COLON DEFINITION: initialize-td-free-list - Internal Function
349:
350: \ Initialize the TD Free List Region and create a linked list OF successive
351: \ TDs. Note that the NEXT pointers are all in little-endian and they
352: \ can be directly used for HC purposes.
353:
354:
355: : initialize-td-free-list ( -- )
356: MAX-TDS 0= IF EXIT THEN
357: td-list-region @ 0= IF EXIT THEN
358: td-list-region @ TO temp1
359: 0 TO temp2 BEGIN
360: temp1 zero-out-a-td-except-link
361: temp1 /tdlen + dup temp1 td>ntd l!-le TO temp1
362: temp2 1+ TO temp2
363: temp2 MAX-TDS = ( TRUE | FALSE )
364: UNTIL
365: temp1 /tdlen - dup 0 swap td>ntd l!-le TO td-freelist-tail
366: td-list-region @ TO td-freelist-head
367: MAX-TDS TO num-free-tds
368: ;
369:
370:
371: \ COLON DEFINITION: allocate-td-list -- Internal function
372: \ Argument:
373: \ The function accepts a non-negative number and allocates
374: \ a TD-LIST containing that many TDs. A TD-LIST is a list
375: \ OF TDs that are linked by the next-td field. The next-td
376: \ field is in little-endian mode so that the TD list can
377: \ be directly re-used by the HC.
378: \ Return value:
379: \ The function returns "head" and "tail" OF the allocated
380: \ TD-LIST. If for any reason, the function cannot allocate
381: \ the TD-LIST, the function returns 2 NULL pointers in the
382: \ stack indicating that the allocation failed.
383:
384: \ Note that the TD list returned is NULL terminated. i.e
385: \ the nextTd field OF the tail is NULL.
386:
387:
388:
389: : allocate-td-list ( n -- head tail )
390: dup 0= IF drop 0 0 EXIT THEN ( 0 0 )
391: dup num-free-tds > IF drop 0 0 EXIT THEN ( 0 0 )
392: dup num-free-tds = IF ( n )
393: drop td-freelist-head td-freelist-tail ( td-freelist-head td-freelist-tail )
394: 0 TO td-freelist-head ( td-freelist-head td-freelist-tail )
395: 0 TO td-freelist-tail ( td-freelist-head td-freelist-tail )
396: 0 TO num-free-tds ( td-freelist-head td-freelist-tail )
397: EXIT
398: THEN
399:
400: \ If we are here THEN we know that the requested number OF TDs is less
401: \ than what we actually have. We need TO traverse the list and find the
402: \ new Head pointer position and THEN update the head pointer accordingly.
403: \ Update num-free-tds
404:
405: dup num-free-tds swap - TO num-free-tds ( n )
406:
407: \ Traverse through the Free list to identify the element that exists after
408: \ "n" TDs. Use the info to return the head and tail pointer and update
409: \ the new td-list-head
410:
411: td-freelist-head ( n td-list-head )
412: dup TO temp1 ( n td-list-head )
413: swap ( td-list-head n )
414: 0 DO ( td-list-head )
415: temp1 TO temp2 ( td-list-head )
416: temp1 td>ntd l@-le TO temp1 ( td-list-head )
417: LOOP ( td-list-head )
418: temp2 ( td-list-head td-list-tail )
419: dup td>ntd 0 swap l!-le ( td-list-head td-list-tail )
420: temp1 TO td-freelist-head ( td-list-head td-list-tail )
421: ;
422:
423:
424: \ COLON DEFINITION: find-td-list-tail-and-size
425: \ This function counts the number OF TD elements
426: \ in the given list. It also returns the last tail
427: \ TD OF the TD list.
428:
429: \ ASSUMPTION:
430: \ A NULL terminated TD list is assumed. A not-well formed
431: \ list can result in in-determinate behaviour.
432:
433: \ ROOM FOR ENHANCEMENT:
434: \ We could arrive at a generic function for counting
435: \ list elements to which the next-ptr OFfset can also
436: \ be passed as an argument (in this case it is >ntd)
437: \ This function can THEN be changed to call the
438: \ function with "0 >ntd" as an additional argument
439: \ (apart from head and tail)
440:
441:
442: : find-td-list-tail-and-size ( head -- tail n )
443: TO temp1
444: 0 TO temp2
445: 0 TO temp3
446: DEBUG-TDS IF
447: s" BEGIN find-td-list-tail-and-size: " usb-debug-print
448: THEN
449: BEGIN
450: temp1 0<> ( TRUE|FALSE )
451: WHILE
452: DEBUG-TDS IF
453: temp1 u. cr
454: THEN
455: temp1 TO temp3
456: temp1 td>ntd l@-le TO temp1
457: temp2 1+ TO temp2
458: REPEAT
459: temp3 temp2 ( tail n )
460: DEBUG-TDS IF
461: s" END find-td-list-tail-and-size" usb-debug-print
462: THEN
463: ;
464:
465:
466: \ COLON DEFINITION: (free-td-list)
467:
468: \ Arguments: (head --)
469: \ The "head" pointer OF the TD-LIST to be freed is passed as
470: \ an argument to this function. The function merely adds the list to the
471: \ already existing TD-LIST
472:
473: \ Assumptions:
474: \ The function assumes that the TD-LIST passed as argument is a well-formed
475: \ list. The function does not DO any check on it.
476: \ But since, the "TD-LIST" is generally freed from the DONE-QUEUE which is
477: \ a well-formed list, the interface makes much sense.
478:
479: \ Return values:
480: \ Nothing is returned. The arguments passed are popped OFf.
481:
482:
483: : (free-td-list) ( head -- )
484:
485: \ Enhancement:
486: \ We could zero-out-a-td-except-link for the TD list that is being freed.
487: \ This way, we could prevent some nasty repercussions OF bugs (that r yet
488: \ to be discovered). but we can include this enhancement during the testing
489: \ phase.
490:
491: dup find-td-list-tail-and-size num-free-tds + TO num-free-tds ( head tail )
492: td-freelist-tail 0= IF ( head tail )
493: dup TO td-freelist-tail ( head tail )
494: THEN ( head tail )
495: td>ntd td-freelist-head swap l!-le ( head )
496: TO td-freelist-head
497: ;
498:
499:
500: \ END OF TD LIST MANAGEMENT WORDS
501: \ ED Management section BEGINs
502: \ ----------------------------
503:
504:
505: : zero-out-an-ed-except-link ( ed -- )
506:
507: \ There are definitely smarter ways to do it especially
508: \ on a 64-bit machine.
509:
510: \ Optimization, Portability:
511: \ --------------------------
512: \ Replace by a "!" and "l!". we know that an "ed" is
513: \ actually 16 bytes and that we will be executing on
514: \ a 64-bit machine, we can finish OFf with 2 stores.
515: \ But that WONT be portable.
516:
517: dup 0 swap ed>eattr l!-le ( ed )
518: dup 0 swap ed>tdqtp l!-le ( ed )
519: dup 0 swap ed>tdqhp l!-le ( ed )
520: drop
521: ;
522:
523: \ Intialises ed-list afresh
524:
525: : initialize-ed-free-list ( -- )
526: MAX-EDS 0= IF EXIT THEN
527: ed-list-region @ 0= IF
528: s" init-ed-list: ed-list-region is not allocated!" usb-debug-print
529: EXIT
530: THEN
531: ed-list-region @ TO temp1
532: 0 TO temp2 BEGIN
533: temp1 zero-out-an-ed-except-link
534: temp1 /edlen + dup temp1 ed>ned l!-le TO temp1
535: temp2 1+ TO temp2
536: temp2 MAX-EDS =
537: UNTIL
538: temp1 /edlen - ed>ned 0 swap l!-le
539: ed-list-region @ TO ed-freelist-head
540: MAX-EDS TO num-free-eds
541: ;
542:
543:
544: \ allocate an ed and return ed address
545:
546:
547: : allocate-ed ( -- ed-ptr )
548: num-free-eds 0= IF 0 EXIT THEN
549: ed-freelist-head ( ed-freelist-head )
550: ed-freelist-head ed>ned l@-le TO ed-freelist-head ( ed-freelist-head )
551: num-free-eds 1- TO num-free-eds ( ed-freelist-head )
552: dup ed>ned 0 swap l!-le \ Terminate the Link. ( ed-freelist-head )
553: ;
554:
555:
556: \ free the given ed pointer
557:
558: : free-ed ( ed-ptr -- )
559: dup zero-out-an-ed-except-link ( ed-ptr )
560: dup ed>ned ed-freelist-head swap l!-le ( ed-ptr )
561: TO ed-freelist-head
562: num-free-eds 1+ TO num-free-eds
563: ;
564:
565:
566: \ Buffer allocations
567: \ ------------------
568: \ Note:
569: \ -----
570: \ 1. What should we DO IF alloc-mem fails ?
571: \ 2. alloc-mem must return aligned memory addresses.
572: \ 3. alloc-mem must return DMAable memory!
573:
574: \ Memory for the HCCA - must stay allocated as long as the HC is operational!
575: 100 alloc-mem VALUE hchcca
576: hchcca ff and IF
577: \ This should never happen - alloc-mem always aligns
578: s" Warning: hchcca not aligned!" usb-debug-print
579: THEN
580:
581: 84 hchcca + CONSTANT hchccadneq
582:
583:
584: : (allocate-mem) ( -- )
585: /tdlen MAX-TDS * 10 + alloc-mem dup td-list-region ! ( td-list-region-ptr )
586: f and IF
587: s" Warning: td-list-region not aligned!" usb-debug-print
588: THEN
589: initialize-td-free-list
590:
591: /edlen MAX-EDS * 10 + alloc-mem dup ed-list-region ! ( ed-list-region-ptr )
592: f and IF
593: s" Warning: ed-list-region not aligned!" usb-debug-print
594: THEN
595: initialize-ed-free-list
596:
597: DEVICE-DESCRIPTOR-LEN chars alloc-mem dd-buffer !
598: BULK-CONFIG-DESCRIPTOR-LEN chars alloc-mem cd-buffer !
599: ;
600:
601:
602: \ The method makes sure that when the host node is closed all
603: \ associated buffer allocations made for data-structures as
604: \ well as data-buffers are freed
605:
606: : (de-allocate-mem) ( -- )
607: td-list-region @ ?dup IF
608: /tdlen MAX-TDS * 10 + free-mem
609: 0 td-list-region !
610: THEN
611: ed-list-region @ ?dup IF
612: /edlen MAX-EDS * 10 + free-mem
613: 0 ed-list-region !
614: THEN
615: dd-buffer @ ?dup IF
616: DEVICE-DESCRIPTOR-LEN free-mem
617: 0 dd-buffer !
618: THEN
619: cd-buffer @ ?dup IF
620: BULK-CONFIG-DESCRIPTOR-LEN free-mem
621: 0 cd-buffer !
622: THEN
623: ;
624:
625:
626: \ Suspend hostcontroller (and the bus).
627: \ This method must be called before the operating system starts.
628: \ It prevents the HC from doing DMA in the background during boot
629: \ (e.g. updating its frame number counter in the HCCA)
630:
631: : hc-suspend ( -- )
632: \ s" USB HC suspend with hccontrol=" type hccontrol . cr
633: 00C3 hccontrol rl!-le \ Suspend USB host controller
634: ;
635:
636:
637: \ OF methods
638:
639: : open ( -- TRUE|FALSE )
640: (allocate-mem)
641: TRUE
642: ;
643:
644: : close ( -- )
645: (de-allocate-mem)
646: ;
647:
648:
649: \ COLON DEFINITION: HC-enable-control-list-processing
650: \ Enables USB HC transactions on control list.
651:
652: : HC-enable-control-list-processing ( -- )
653: hccomstat dup rl@-le 02 or swap rl!-le
654: hccontrol dup rl@-le 10 or swap rl!-le
655: ;
656:
657:
658: \ COLON DEFINTION: HC-enable-bulk-list-processing
659: \ PENDING: Remove Hard coded constants.
660:
661: : HC-enable-bulk-list-processing ( -- )
662: hccomstat dup rl@-le 04 or swap rl!-le
663: hccontrol dup rl@-le 20 or swap rl!-le
664: ;
665:
666:
667: : HC-enable-interrupt-list-processing ( -- )
668: hccontrol dup rl@-le 04 or swap rl!-le
669: ;
670:
671:
672: \ Clearing WDH to allow HC to write into DOne queue again
673:
674: : (HC-ACK-WDH) ( -- ) WDH hcintstat rl!-le ;
675:
676: \ Checking whether anything has been written into DOne queue
677:
678: : (HC-CHECK-WDH) ( -- ) hcintstat rl@-le WDH and 0<> ;
679:
680:
681: \ Disable USB transaction and keep it ready
682:
683: : disable-control-list-processing ( -- )
684: hccontrol dup rl@-le ffffffef and swap rl!-le
685: hccomstat dup rl@-le fffffffd and swap rl!-le
686: ;
687:
688: : disable-bulk-list-processing ( -- )
689: hccontrol dup rl@-le ffffffdf and swap rl!-le
690: hccomstat dup rl@-le fffffffb and swap rl!-le
691: ;
692:
693:
694: : disable-interrupt-list-processing ( -- )
695: hccontrol dup rl@-le fffffffb and swap rl!-le
696: ;
697:
698:
699: \ COLON DEFINITION: fill-TD-list
700:
701: \ This function accepts a TD list and a data-buffer and
702: \ distributes this data buffer over the TD list depending
703: \ on the Max Packet Size.
704:
705: \ Arguments:
706: \ ----------
707: \ (from bottom OF stack)
708: \ 1. addr -- Address OF the data buffer
709: \ 2. dlen -- Length OF the data buffer above.
710: \ 3. dir -- Tells whether the TDs r for an IN or
711: \ OUT transaction.
712: \ 4. MPS -- Maximum Packet Size associated with the endpoint
713: \ that will use this TD list.
714: \ 5. TD-List-Head - Head pointer OF the List OF TDs.
715: \ This list is NOT expected to be NULL terminated.
716:
717: \ Assumptions:
718: \ -----------
719: \ 1. TD-List for data is well-formed and has sufficient entries
720: \ to hold "dlen".
721: \ 2. The TDs toggle field is assumed to be taken from the endpoint
722: \ descriptor's "toggle carry" field.
723: \ 3. Assumes that the caller specifies the correct start-toggle.
724: \ If the caller specifies a wrong data toggle OF 1 for a SETUP
725: \ PACKET, this method will not find it out.
726:
727: \ COLON DEFINTION: (toggle-current-toggle)
728: \ Scope: Internal to fill-TD-list
729: \ Functionality:
730: \ Toggles the "T" field that is passed as argument.
731: \ "T" as in the "T" field OF the TD.
732:
733: 0 VALUE current-toggle
734: : fill-TD-list ( start-toggle addr dlen dp MPS TD-List-Head -- )
735: TO temp1 ( start-toggle addr dlen dp MPS )
736: TO temp2 ( start-toggle addr dlen dp )
737: CASE ( start-toggle addr dlen )
738: OHCI-DP-SETUP OF TD-DP-SETUP TO temp3 ENDOF ( start-toggle addr dlen )
739: OHCI-DP-IN OF TD-DP-IN TO temp3 ENDOF ( start-toggle addr dlen )
740: OHCI-DP-OUT OF TD-DP-OUT TO temp3 ENDOF ( start-toggle addr dlen )
741: dup OF -1 TO temp3 ( start-toggle addr dlen )
742: s" fill-TD-list: Invalid DP specified" usb-debug-print
743: ENDOF
744: ENDCASE
745: temp3 -1 = IF EXIT THEN ( start-toggle addr dlen )
746:
747:
748: \ temp1 -- TD-List-Head
749: \ temp2 -- Max Packet Size
750: \ temp3 -- TD-DP-IN or TD-DP-OUT or TD-DP-SETUP
751:
752: rot ( addr dlen start-toggle )
753: TO current-toggle swap ( dlen addr )
754: BEGIN
755: over temp2 >= ( dlen addr TRUE|FALSE )
756: WHILE ( dlen addr )
757: dup temp1 td>cbptr l!-le ( dlen addr )
758: current-toggle 18 lshift ( dlen addr current-toggle~ )
759: DATA0-TOGGLE ( dlen addr current-toggle~ toggle )
760: CC-FRESH-TD temp3 or or or ( dlen addr or-result )
761: temp1 td>tattr l!-le ( dlen addr~ )
762: dup temp2 1- + temp1 td>bfrend l!-le ( dlen addr~ )
763: temp2 + ( dlen next-addr )
764: swap temp2 - swap
765: temp1 td>ntd l@-le TO temp1 ( dlen next-addr )
766: current-toggle ( dlen next-addr current-toggle )
767: CASE
768: 0 OF 1 TO current-toggle ENDOF
769: 1 OF 0 TO current-toggle ENDOF
770: ENDCASE
771: REPEAT ( dlen addr )
772: over 0<> IF
773: dup temp1 td>cbptr l!-le ( dlen addr )
774: current-toggle 18 lshift ( dlen addr curent-toggle~ )
775: DATA0-TOGGLE ( dlen addr curent-toggle~ toggle )
776: CC-FRESH-TD temp3 or or or ( dlen addr or-result )
777: temp1 td>tattr l!-le ( dlen addr )
778: + 1- temp1 td>bfrend l!-le
779: ELSE
780: 2drop
781: THEN
782: ;
783:
784:
785: \ COLON DEFINITION: (td-list-status )
786: \ FUNCTIONALITY:
787: \ To traverse the TD list to check for a TD carrying non-zero CC return the
788: \ respective TD address and CC ELSE 0
789: \ SCOPE:
790: \ Internal method
791:
792: : (td-list-status) ( PointerToTDlist -- failingTD CCode TRUE | 0 )
793: BEGIN ( PointerToTDlist )
794: dup 0<> ( PointerToTDlist TRUE|FALSE )
795: IF ( PointerToTDlist )
796: dup td>tattr l@-le f0000000 and 1c rshift dup 0= TRUE swap
797: ( PointerToTDlist CCode TRUE TRUE|FALSE )
798: ELSE
799: drop FALSE dup ( FALSE )
800: THEN
801: WHILE
802: drop drop td>ntd l@-le
803: REPEAT
804: ;
805:
806:
807: \ ==================================================================
808: \ COLON DEFINITION: (wait-for-done-q)
809: \ FUNCTIONALITY:
810: \ To DO a timed polling OF the DOne queue and acknowledge and return
811: \ the address OF the last retired Td list
812: \ SCOPE:
813: \ Internal method
814: \ ==================================================================
815:
816: : (wait-for-done-q) ( timeout -- TD-list TRUE | FALSE )
817: BEGIN ( timeout )
818: dup 0<> ( timeout TRUE|FALSE )
819: (HC-CHECK-WDH) NOT ( timeout TRUE|FALSE TRUE|FALSE )
820: AND \ not timed out AND WDH-bit not set
821: WHILE
822: 1 ms \ wait
823: 1- ( timeout )
824: dup ff and 0= IF show-proceed THEN
825: REPEAT ( timeout )
826: drop
827: hchccadneq l@-le \ read last HcDoneHead (RAM)
828: (HC-CHECK-WDH) \ HcDoneHead was updated ?
829: IF
830: (HC-ACK-WDH) \ clear register bit: WDH
831: TRUE ( td-list TRUE )
832: ELSE
833: FALSE
834: THEN
835: ;
836:
837:
838: \ displays free tds
839:
840:
841: : debug-td ( -- )
842: s" Num Free TDs = " num-free-tds usb-debug-print-val
843: ;
844:
845:
846: \ display content of frame counter
847:
848: \ : debug-frame-counter ( -- )
849: \ 40 1 DO
850: \ ." Frame ct at HCCA at end OF enumeration = "
851: \ hchcca 80 + rl@-le .
852: \ LOOP
853: \ ;
854:
855: \ ============================================================================
856: \ COLON DEFINITION: HC-reset
857: \ This routine should be the first to be executed.
858: \ This routine will reset the HC and will bring it to Operational
859: \ state.
860: \ PENDING:
861: \ Arrive at the right value OF FrameInterval. Currently we are hardcoding
862: \ it.
863: \ ==========================================================================
864: : HC-reset ( -- )
865:
866: hccomstat dup rl@-le 01 or swap rl!-le \ issue HC reset
867: BEGIN
868: hccomstat rl@-le 01 and 0<> \ wait for reset end
869: WHILE
870: REPEAT
871:
872: 23f02edf hcintrval rl!-le \ frame-interval register
873: hchcca hchccareg rl!-le \ HC communication area
874: 0000 hcctrhead rl!-le \ control transfer head
875: 0000 hcbulkhead rl!-le \ bulk transfer head
876: 0ffff hcintdsbl rl!-le \ interrupt disable reg.
877:
878: \ all devices are still in reset-state
879: \ next command starts sending SOFs
880: 83 hccontrol rl!-le \ set USBOPERATIONAL
881:
882: \ these two repeated register settings are necessary for Bimini
883: \ Its OHCI controller (AM8111) behaves different to NEC's one
884: 23f02edf hcintrval rl!-le \ frame-interval register
885: hchcca hchccareg rl!-le \ HC communication area
886:
887: d# 50 ms
888:
889: hcrhdescA rl@-le ff and ( total-rh-ports )
890: to max-rh-ports
891:
892: \ if no hardware-reset was issued (rescan)
893: \ switch off all ports first !
894: hcrhpstat TO current-stat \ start with first port status reg
895: 0 \ port status default
896: max-rh-ports 0 \ checking all ports
897: DO
898: current-stat rl@-le or \ OR-ing all stats
899: 200 current-stat rl!-le \ Clear Port Power (CPP)
900: current-stat 4 + TO current-stat \ check next RH-Port
901: LOOP
902: 100 and 0<> \ any of the ports had power ?
903: IF
904: d# 750 wait-proceed \ wait for power discharge
905: THEN
906:
907: \ now power on all ports of this root-hub
908: hcrhpstat TO current-stat \ start with first port status reg
909: max-rh-ports 0
910: DO
911: 102 current-stat rl!-le \ power on and enable
912: hcrhdescA 3 + rb@ 2 * ms \ startup delay 30 ms (2 * POTPGT)
913: current-stat 4 + TO current-stat \ check next RH-Port
914: LOOP
915: d# 500 wait-proceed \ STEC device needs 300 ms
916: ;
917:
918: : error-recovery ( -- )
919: initialize-td-free-list
920: initialize-ed-free-list
921: HC-reset
922: ;
923:
924: \ ================================================================
925: : store-initial-usb-hub-address ( -- )
926: usb-address TO initial-hub-address
927: ;
928:
929: : reset-to-initial-usb-hub-address ( -- )
930: initial-hub-address TO usb-address
931: ;
932:
933: \ allocate-usb-address:
934: \ Function allocates an USB address.
935: \ See RISK below.
936:
937:
938: : allocate-usb-address ( -- usb-address )
939: usb-address 7f <> ( TRUE|FALSE )
940: IF
941: usb-address 1+ TO usb-address \ RISK: Check to see IF it overflows 127
942: usb-address ( usb-address )
943: THEN ( usb-address )
944: ;
945:
946: s" usb-support.fs" INCLUDED
947:
948:
949:
950: \ =====================================================================
951: \ COLON DEFINTION: control-std-set-address
952: \ INTERFACE FUNCTION
953: \ Function allocates an USB addrss and uses it to send SET-ADDRESS packet
954: \ to the default USB address.
955: \ This is an interface function available to child nodes.
956:
957: : control-std-set-address ( speedbit -- usb-address TRUE | FALSE )
958: >r ( R: speedbit )
959: 0005000000000000 setup-packet !
960: allocate-usb-address dup setup-packet 2 + c! ( usb-addr R: speedbit )
961: s" USB set-address: " 2 pick usb-debug-print-val ( usb-addr R: speedbit )
962: 0 0 0 setup-packet 8 r> controlxfer ( usb-addr TRUE | FALSE )
963: IF ( TRUE | FALSE )
964: TRUE ( TRUE )
965: ELSE
966: drop FALSE \ PENDING: Return the allocated address back. ( FALSE )
967: THEN ( TRUE | FALSE )
968: ;
969:
970:
971: \ Fetches the device decriptor of the usb-device
972:
973:
974: : control-std-get-device-descriptor
975: ( data-buffer data-len MPS fa -- TRUE|FALSE )
976:
977: 8006000100000000 setup-packet !
978: 2 pick setup-packet 6 + w!-le
979: ( data-buffer data-len MPS fa )
980: setup-packet -rot ( data-buffer data-len setup-packet MPS fa )
981: >r >r >r >r >r 0 r> r> r> r> r>
982: ( 0 data-buffer data-len setup-packet MPS fa )
983: controlxfer ( TRUE | FALSE )
984: ;
985:
986:
987: \ ==================================================================
988: \ To retrieve the configuration descriptor OF a device
989: \ with a valid USB address
990:
991:
992: : control-std-get-configuration-descriptor
993: ( data-buffer data-len MPS FuncAddr -- TRUE|FALSE )
994: TO temp1 ( data-buffer data-len MPS )
995: TO temp2 ( data-buffer data-len )
996: TO temp3 ( data-buffer )
997: 8006000200000000 setup-packet !
998: temp3 setup-packet 6 + w!-le
999: 0 swap temp3 setup-packet temp2 temp1 controlxfer
1000: ;
1001:
1002: \ Fetches num of logical units available for a device
1003: : control-std-get-maxlun ( MPS fun-addr dir data-buff data-len -- TRUE | FALSE )
1004: GET-MAX-LUN setup-packet ! ( MPS fun-addr dir data-buff data-len )
1005: setup-packet 5 pick 5 pick
1006: ( MPS fun-addr dir data-buff data-len setup-packet MPS fun-addr )
1007: controlxfer ( MPS fun-addr TRUE | FALSE )
1008: nip nip ( TRUE | FALSE )
1009: ;
1010:
1011: \ Bulk-Only Mass Storage Reset
1012: \ fixed to interface #0
1013: : control-bulk-reset ( MPS fun-addr dir data-buff data-len -- TRUE | FALSE )
1014: 21FF000000000000 setup-packet ! ( MPS fun-addr dir data-buff data-len )
1015: setup-packet 5 pick 5 pick
1016: ( MPS fun-addr dir data-buff data-len setup-packet MPS fun-addr )
1017: controlxfer ( MPS fun-addr TRUE | FALSE )
1018: nip nip ( TRUE | FALSE )
1019: ;
1020:
1021:
1022:
1023: \ get the string descriptor of the usb device
1024:
1025:
1026: : control-std-get-string-descriptor
1027: ( StringIndex data-buffer data-len MPS FuncAddr -- TRUE | FALSE )
1028: TO temp1 ( StringIndex data-buffer data-len MPS )
1029: TO temp2 ( StringIndex data-buffer data-len )
1030: TO temp3 ( StringIndex )
1031: 8006000300000000 setup-packet !
1032: temp3 setup-packet 6 + w!-le
1033: 409 setup-packet 4 + w!-le \ US English Language code.
1034: swap ( data buffer StringIndex )
1035: setup-packet 2 + c! ( data-buffer )
1036: 0 swap temp3 setup-packet temp2 temp1 controlxfer ( TRUE | FALSE )
1037: ;
1038:
1039: \ sets a valid usb configaration for a device
1040:
1041: : control-std-set-configuration ( configvalue FuncAddr -- TRUE|FALSE )
1042: TO temp1 ( configvalue )
1043: TO temp2
1044: 0009000000000000 setup-packet ! \ RISK: Endian and 64-bit assumptions
1045: temp2 setup-packet 2 + w!-le
1046: 0 0 0 setup-packet DEFAULT-CONTROL-MPS temp1 controlxfer
1047:
1048: \ NOTE: We could use DEFAULT-CONTROL-MPS because there is no data phase
1049: \ associated with this control xfer. Its a dont care.
1050: ;
1051:
1052:
1053: \ To set the device address retrive the device descriptor and build the
1054: \ usb device tree by passing device class
1055:
1056:
1057: 0 VALUE port-number
1058:
1059: s" usb-enumerate.fs" INCLUDED
1060:
1061: : rhport-enumerate ( port-num -- )
1062: TO port-number
1063: device-speed control-std-set-address ( usb-addr TRUE | FALSE )
1064: IF
1065: device-speed or ( usb-addr+speedbit )
1066: TO new-device-address
1067: dd-buffer @ 8 erase
1068:
1069: \ Read Device Descriptor - First 8 bytes.
1070:
1071: dd-buffer @ DEFAULT-CONTROL-MPS DEFAULT-CONTROL-MPS ( buffer mps mps )
1072: new-device-address control-std-get-device-descriptor ( TRUE | FALSE )
1073: IF
1074: ELSE
1075: s" USB: Read Dev Descriptor failed" usb-debug-print EXIT
1076:
1077: \ NOTE: Tomorrow, IF there is a LOOP here,we may need to UNLOOP before
1078: \ "EXIT"ing. Beware. Much depends on what LOOPing construct is used.
1079:
1080: THEN
1081:
1082: \ Read the Descriptor Type and check IF we have read correctly.
1083:
1084: dd-buffer @ DEVICE-DESCRIPTOR-TYPE-OFFSET + c@ ( Descriptor-type )
1085: DEVICE-DESCRIPTOR-TYPE <> IF
1086: s" USB: Error Reading Device Descriptor" usb-debug-print
1087: s" Read descriptor is not OF the right type" usb-debug-print
1088: s" Aborting enumeration" usb-debug-print
1089: EXIT
1090: \ NOTE: Tomorrow, IF u have a LOOP here THEN we may need to
1091: \ UNLOOP before EXITing. Depends on what type OF LOOPing construct
1092: \ is used. Beware.
1093:
1094: THEN
1095:
1096: \ Read the MPS and store it.
1097:
1098: dd-buffer @ DEVICE-DESCRIPTOR-MPS-OFFSET + c@ TO mps
1099:
1100: \ NOTE: Probably, we could check MPS for only 8/16/32/64
1101: \ hmm.. not now...
1102:
1103: \ Read the device class to see what type OF device it is and create an
1104: \ appropriate child node here.
1105: create-usb-device-tree
1106: ELSE
1107: s" Set address failed on port " port-number usb-debug-print-val
1108: s" Aborting Enumeration." usb-debug-print
1109: EXIT
1110:
1111: \ NOTE: Tomorrow , IF u have a LOOP here THEN we may need to
1112: \ UNLOOP before EXITing. Depends on what type OF LOOPing construct
1113: \ is used. Beware.
1114:
1115: THEN
1116: ;
1117:
1118:
1119: \ =========================================================================
1120: \ PROTOTYPE FUNCTION: "rhport-initialize"
1121: \ Detect Device, reset and enable the respective port.
1122: \ COLON Definition rhport-initialize accepts the total number OF root hub
1123: \ ports as an argument and probes every available root hub port and initiates
1124: \ the build OF the USB devie sub-tree so is effectively the mother OF all
1125: \ USB device nodes that are to be detected and instantiated.
1126: \ ==========================================================================
1127: : rhport-initialize ( -- )
1128:
1129: hcrhpstat TO current-stat \ start with first port status reg
1130: max-rh-ports 1+ 1
1131: DO
1132: \ any Device connected to that port ?
1133: current-stat rl@-le RHP-CCS and 0<> ( TRUE|FALSE )
1134: IF
1135: current-stat hcrhpstat3 = \ third port of NEC ?
1136: IF
1137: 81 to uDOC-present \ uDOC is present and now processed
1138: THEN
1139:
1140: s" Device connected to this port!" usb-debug-print
1141: RHP-PRS current-stat rl!-le \ issue a port reset
1142: BEGIN
1143: current-stat rl@-le RHP-PRS AND \ wait for reset end
1144: WHILE
1145: REPEAT
1146: hcrhdescA 3 + rb@ 2 * ms \ startup delay 30 ms (POTPGT)
1147: d# 100 ms
1148:
1149: current-stat rl@-le 200 and 4 lshift
1150: to device-speed \ store speed bit
1151:
1152: RHP-CSC RHP-PRSC or current-stat rl!-le
1153:
1154: I ['] rhport-enumerate CATCH IF \ Scan port
1155: s" USB scan failed on root hub port: " rot usb-debug-print-val
1156: reset-to-initial-usb-hub-address
1157: THEN
1158:
1159: ELSE
1160: s" No device detected at this port." usb-debug-print
1161: current-stat hcrhpstat3 = \ third port of NEC ? (=ModFD)
1162: IF \ here a ModFD should be on ELBA
1163: current-stat rl@-le 80000 and 0<> \ is over-current detected ?
1164: IF
1165: uDOC-present 08 or to uDOC-present \ set flag for uDOC-check
1166: THEN
1167: THEN
1168: THEN
1169: current-stat 4 + TO current-stat \ check next RH-Port
1170: uDOC-present 0f and to uDOC-present \ remove processing flag
1171: LOOP
1172: ;
1173:
1174:
1175: \ ===================================================
1176: \ Enumeration at Host level
1177: \ ===================================================
1178:
1179: : enumerate ( -- )
1180: HC-reset
1181: ['] hc-suspend add-quiesce-xt \ Assert that HC will be supsended
1182: store-initial-usb-hub-address
1183: rhport-initialize \ Probe all available RH ports
1184: reset-to-initial-usb-hub-address
1185: ;
1186:
1187:
1188: \ Create an alias for this controller:
1189: set-ohci-alias
1190:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.