Subversion Repositories Kolibri OS

Rev

Rev 5363 | Details | Compare with Previous | Last modification | View Log | RSS feed

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