Subversion Repositories Kolibri OS

Rev

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

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