Subversion Repositories Kolibri OS

Rev

Rev 3534 | Rev 4418 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3520 clevermous 1
; Initialization of the USB subsystem.
2
; Provides usb_init procedure, includes all needed files.
3
 
4
; General notes:
5
; * There is one entry point for external kernel code: usb_init is called
6
;   from initialization code and initializes USB subsystem.
7
; * There are several entry points for API; see the docs for description.
8
; * There are several functions which are called from controller-specific
9
;   parts of USB subsystem. The most important is usb_new_device,
10
;   which is called when a new device has been connected (over some time),
11
;   has been reset and is ready to start configuring.
12
; * IRQ handlers are very restricted. They can not take any locks,
13
;   since otherwise a deadlock is possible: imagine that a code has taken the
14
;   lock and was interrupted by IRQ handler. Now IRQ handler would wait for
15
;   releasing the lock, and a lock owner would wait for exiting IRQ handler
16
;   to get the control.
17
; * Thus, there is the special USB thread which processes almost all activity.
18
;   IRQ handlers do the minimal processing and wake this thread.
19
; * Also the USB thread wakes occasionally to process tasks which can be
20
;   predicted without interrupts. These include e.g. a periodic roothub
21
;   scanning in UHCI and initializing in USB_CONNECT_DELAY ticks
22
;   after connecting a new device.
23
; * The main procedure of USB thread, usb_thread_proc, does all its work
24
;   by querying usb_hardware_func.ProcessDeferred for every controller
25
;   and usb_hub_process_deferred for every hub.
26
;   ProcessDeferred does controller-specific actions and calculates the time
27
;   when it should be invoked again, possibly infinite.
28
;   usb_thread_proc selects the minimum from all times returned by
29
;   ProcessDeferred and sleeps until this moment is reached or the thread
30
;   is awakened by IRQ handler.
31
 
32
; Initializes the USB subsystem.
33
proc usb_init
34
; 1. Initialize all locks.
35
        mov     ecx, usb_controllers_list_mutex
36
        call    mutex_init
37
        mov     ecx, usb1_ep_mutex
38
        call    mutex_init
39
        mov     ecx, usb_gtd_mutex
40
        call    mutex_init
41
        mov     ecx, ehci_ep_mutex
42
        call    mutex_init
43
        mov     ecx, ehci_gtd_mutex
44
        call    mutex_init
45
; 2. Kick off BIOS from all USB controllers, calling the corresponding function
46
; *hci_kickoff_bios. Also count USB controllers for the next step.
47
; Note: USB1 companion(s) must go before the corresponding EHCI controller,
48
; otherwise BIOS could see a device moving from EHCI to a companion;
49
; first, this always wastes time;
50
; second, some BIOSes are buggy, do not expect that move and try to refer to
51
; previously-assigned controller instead of actual; sometimes that leads to
52
; hangoff.
53
; Thus, process controllers in PCI order.
54
        mov     esi, pcidev_list
55
        push    0
56
.kickoff:
57
        mov     esi, [esi+PCIDEV.fd]
58
        cmp     esi, pcidev_list
59
        jz      .done_kickoff
60
        cmp     word [esi+PCIDEV.class+1], 0x0C03
61
        jnz     .kickoff
62
        mov     eax, uhci_kickoff_bios
63
        cmp     byte [esi+PCIDEV.class], 0x00
64
        jz      .do_kickoff
65
        mov     eax, ohci_kickoff_bios
66
        cmp     byte [esi+PCIDEV.class], 0x10
67
        jz      .do_kickoff
68
        mov     eax, ehci_kickoff_bios
69
        cmp     byte [esi+PCIDEV.class], 0x20
70
        jnz     .kickoff
71
.do_kickoff:
72
        inc     dword [esp]
73
        call    eax
74
        jmp     .kickoff
75
.done_kickoff:
76
        pop     eax
77
; 3. If no controllers were found, exit.
78
; Otherwise, run the USB thread.
79
        test    eax, eax
80
        jz      .nothing
81
        call    create_usb_thread
82
        jz      .nothing
83
; 4. Initialize all USB controllers, calling usb_init_controller for each.
84
; Note: USB1 companion(s) should go before the corresponding EHCI controller,
85
; although this is not strictly necessary (this way, a companion would not try
86
; to initialize high-speed device only to see a disconnect when EHCI takes
87
; control).
88
; Thus, process all EHCI controllers in the first loop, all USB1 controllers
89
; in the second loop. (One loop in reversed PCI order could also be used,
90
; but seems less natural.)
91
; 4a. Loop over all PCI devices, call usb_init_controller
92
; for all EHCI controllers.
93
        mov     eax, pcidev_list
94
.scan_ehci:
95
        mov     eax, [eax+PCIDEV.fd]
96
        cmp     eax, pcidev_list
97
        jz      .done_ehci
98
        cmp     [eax+PCIDEV.class], 0x0C0320
99
        jnz     .scan_ehci
100
        mov     edi, ehci_hardware_func
101
        call    usb_init_controller
102
        jmp     .scan_ehci
103
.done_ehci:
104
; 4b. Loop over all PCI devices, call usb_init_controller
105
; for all UHCI and OHCI controllers.
106
        mov     eax, pcidev_list
107
.scan_usb1:
108
        mov     eax, [eax+PCIDEV.fd]
109
        cmp     eax, pcidev_list
110
        jz      .done_usb1
111
        mov     edi, uhci_hardware_func
112
        cmp     [eax+PCIDEV.class], 0x0C0300
113
        jz      @f
114
        mov     edi, ohci_hardware_func
115
        cmp     [eax+PCIDEV.class], 0x0C0310
116
        jnz     .scan_usb1
117
@@:
118
        call    usb_init_controller
119
        jmp     .scan_usb1
120
.done_usb1:
121
.nothing:
122
        ret
123
endp
124
 
125
uglobal
126
align 4
127
usb_event       dd      ?
128
endg
129
 
130
; Helper function for usb_init. Creates and initializes the USB thread.
131
proc create_usb_thread
132
; 1. Create the thread.
133
        push    edi
3598 clevermous 134
        movi    ebx, 1
3520 clevermous 135
        mov     ecx, usb_thread_proc
136
        xor     edx, edx
137
        call    new_sys_threads
138
        pop     edi
139
; If failed, say something to the debug board and return with ZF set.
140
        test    eax, eax
141
        jns     @f
142
        DEBUGF 1,'K : cannot create kernel thread for USB, error %d\n',eax
143
.clear:
144
        xor     eax, eax
145
        jmp     .nothing
146
@@:
147
; 2. Wait while the USB thread initializes itself.
148
@@:
149
        call    change_task
150
        cmp     [usb_event], 0
151
        jz      @b
152
; 3. If initialization failed, the USB thread sets [usb_event] to -1.
153
; Return with ZF set or cleared corresponding to the result.
154
        cmp     [usb_event], -1
155
        jz      .clear
156
.nothing:
157
        ret
158
endp
159
 
160
; Helper function for IRQ handlers. Wakes the USB thread if ebx is nonzero.
161
proc usb_wakeup_if_needed
162
        test    ebx, ebx
163
        jz      usb_wakeup.nothing
164
usb_wakeup:
165
        xor     edx, edx
166
        mov     eax, [usb_event]
167
        mov     ebx, [eax+EVENT.id]
168
        xor     esi, esi
169
        call    raise_event
170
.nothing:
171
        ret
172
endp
173
 
174
; Main loop of the USB thread.
175
proc usb_thread_proc
176
; 1. Initialize: create event to allow wakeup by interrupt handlers.
177
        xor     esi, esi
178
        mov     ecx, MANUAL_DESTROY
179
        call    create_event
180
        test    eax, eax
181
        jnz     @f
182
; If failed, set [usb_event] to -1 and terminate myself.
183
        dbgstr 'cannot create event for USB thread'
184
        or      [usb_event], -1
185
        jmp     sys_end
186
@@:
187
        mov     [usb_event], eax
188
        push    -1              ; initial timeout: infinite
189
usb_thread_wait:
190
; 2. Main loop: wait for either wakeup event or timeout.
191
        pop     ecx             ; get timeout
192
        mov     eax, [usb_event]
193
        mov     ebx, [eax+EVENT.id]
194
        call    wait_event_timeout
195
        push    -1              ; default timeout: infinite
196
; 3. Main loop: call worker functions of all controllers;
197
; if some function schedules wakeup in timeout less than the current value,
198
; replace that value with the returned timeout.
199
        mov     esi, usb_controllers_list
200
@@:
201
        mov     esi, [esi+usb_controller.Next]
202
        cmp     esi, usb_controllers_list
203
        jz      .controllers_done
204
        mov     eax, [esi+usb_controller.HardwareFunc]
205
        call    [eax+usb_hardware_func.ProcessDeferred]
206
        cmp     [esp], eax
207
        jb      @b
208
        mov     [esp], eax
209
        jmp     @b
210
.controllers_done:
211
; 4. Main loop: call hub worker function for all hubs,
212
; similarly calculating minimum of all returned timeouts.
213
; When done, continue to 2.
214
        mov     esi, usb_hubs_list
215
@@:
216
        mov     esi, [esi+usb_hub.Next]
217
        cmp     esi, usb_hubs_list
218
        jz      usb_thread_wait
219
        call    usb_hub_process_deferred
220
        cmp     [esp], eax
221
        jb      @b
222
        mov     [esp], eax
223
        jmp     @b
224
endp
225
 
226
iglobal
227
align 4
228
usb_controllers_list:
229
        dd      usb_controllers_list
230
        dd      usb_controllers_list
231
usb_hubs_list:
232
        dd      usb_hubs_list
233
        dd      usb_hubs_list
234
endg
235
uglobal
236
align 4
237
usb_controllers_list_mutex      MUTEX
238
endg
239
 
240
include "memory.inc"
241
include "hccommon.inc"
242
include "pipe.inc"
243
include "ohci.inc"
244
include "uhci.inc"
245
include "ehci.inc"
246
include "protocol.inc"
247
include "hub.inc"
248
include "scheduler.inc"