Subversion Repositories Kolibri OS

Rev

Rev 3520 | Rev 3598 | 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
134
        push    1
135
        pop     ebx
136
        mov     ecx, usb_thread_proc
137
        xor     edx, edx
138
        call    new_sys_threads
139
        pop     edi
140
; If failed, say something to the debug board and return with ZF set.
141
        test    eax, eax
142
        jns     @f
143
        DEBUGF 1,'K : cannot create kernel thread for USB, error %d\n',eax
144
.clear:
145
        xor     eax, eax
146
        jmp     .nothing
147
@@:
148
; 2. Wait while the USB thread initializes itself.
149
@@:
150
        call    change_task
151
        cmp     [usb_event], 0
152
        jz      @b
153
; 3. If initialization failed, the USB thread sets [usb_event] to -1.
154
; Return with ZF set or cleared corresponding to the result.
155
        cmp     [usb_event], -1
156
        jz      .clear
157
.nothing:
158
        ret
159
endp
160
 
161
; Helper function for IRQ handlers. Wakes the USB thread if ebx is nonzero.
162
proc usb_wakeup_if_needed
163
        test    ebx, ebx
164
        jz      usb_wakeup.nothing
165
usb_wakeup:
166
        xor     edx, edx
167
        mov     eax, [usb_event]
168
        mov     ebx, [eax+EVENT.id]
169
        xor     esi, esi
170
        call    raise_event
171
.nothing:
172
        ret
173
endp
174
 
175
; Main loop of the USB thread.
176
proc usb_thread_proc
177
; 1. Initialize: create event to allow wakeup by interrupt handlers.
178
        xor     esi, esi
179
        mov     ecx, MANUAL_DESTROY
180
        call    create_event
181
        test    eax, eax
182
        jnz     @f
183
; If failed, set [usb_event] to -1 and terminate myself.
184
        dbgstr 'cannot create event for USB thread'
185
        or      [usb_event], -1
186
        jmp     sys_end
187
@@:
188
        mov     [usb_event], eax
189
        push    -1              ; initial timeout: infinite
190
usb_thread_wait:
191
; 2. Main loop: wait for either wakeup event or timeout.
192
        pop     ecx             ; get timeout
193
        mov     eax, [usb_event]
194
        mov     ebx, [eax+EVENT.id]
195
        call    wait_event_timeout
196
        push    -1              ; default timeout: infinite
197
; 3. Main loop: call worker functions of all controllers;
198
; if some function schedules wakeup in timeout less than the current value,
199
; replace that value with the returned timeout.
200
        mov     esi, usb_controllers_list
201
@@:
202
        mov     esi, [esi+usb_controller.Next]
203
        cmp     esi, usb_controllers_list
204
        jz      .controllers_done
205
        mov     eax, [esi+usb_controller.HardwareFunc]
206
        call    [eax+usb_hardware_func.ProcessDeferred]
207
        cmp     [esp], eax
208
        jb      @b
209
        mov     [esp], eax
210
        jmp     @b
211
.controllers_done:
212
; 4. Main loop: call hub worker function for all hubs,
213
; similarly calculating minimum of all returned timeouts.
214
; When done, continue to 2.
215
        mov     esi, usb_hubs_list
216
@@:
217
        mov     esi, [esi+usb_hub.Next]
218
        cmp     esi, usb_hubs_list
219
        jz      usb_thread_wait
220
        call    usb_hub_process_deferred
221
        cmp     [esp], eax
222
        jb      @b
223
        mov     [esp], eax
224
        jmp     @b
225
endp
226
 
227
iglobal
228
align 4
229
usb_controllers_list:
230
        dd      usb_controllers_list
231
        dd      usb_controllers_list
232
usb_hubs_list:
233
        dd      usb_hubs_list
234
        dd      usb_hubs_list
235
endg
236
uglobal
237
align 4
238
usb_controllers_list_mutex      MUTEX
239
endg
240
 
241
include "memory.inc"
242
include "hccommon.inc"
243
include "pipe.inc"
244
include "ohci.inc"
245
include "uhci.inc"
246
include "ehci.inc"
247
include "protocol.inc"
248
include "hub.inc"
249
include "scheduler.inc"