Subversion Repositories Kolibri OS

Rev

Rev 3598 | Rev 4850 | 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
 
4418 clevermous 32
iglobal
33
uhci_service_name:
34
        db      'UHCI',0
35
ohci_service_name:
36
        db      'OHCI',0
37
ehci_service_name:
38
        db      'EHCI',0
39
endg
40
 
3520 clevermous 41
; Initializes the USB subsystem.
42
proc usb_init
43
; 1. Initialize all locks.
44
        mov     ecx, usb_controllers_list_mutex
45
        call    mutex_init
46
; 2. Kick off BIOS from all USB controllers, calling the corresponding function
47
; *hci_kickoff_bios. Also count USB controllers for the next step.
48
; Note: USB1 companion(s) must go before the corresponding EHCI controller,
49
; otherwise BIOS could see a device moving from EHCI to a companion;
50
; first, this always wastes time;
51
; second, some BIOSes are buggy, do not expect that move and try to refer to
52
; previously-assigned controller instead of actual; sometimes that leads to
53
; hangoff.
54
; Thus, process controllers in PCI order.
55
        mov     esi, pcidev_list
56
        push    0
57
.kickoff:
58
        mov     esi, [esi+PCIDEV.fd]
59
        cmp     esi, pcidev_list
60
        jz      .done_kickoff
61
        cmp     word [esi+PCIDEV.class+1], 0x0C03
62
        jnz     .kickoff
4418 clevermous 63
        mov     ebx, uhci_service_name
3520 clevermous 64
        cmp     byte [esi+PCIDEV.class], 0x00
65
        jz      .do_kickoff
4418 clevermous 66
        mov     ebx, ohci_service_name
3520 clevermous 67
        cmp     byte [esi+PCIDEV.class], 0x10
68
        jz      .do_kickoff
4418 clevermous 69
        mov     ebx, ehci_service_name
3520 clevermous 70
        cmp     byte [esi+PCIDEV.class], 0x20
71
        jnz     .kickoff
72
.do_kickoff:
73
        inc     dword [esp]
4418 clevermous 74
        push    ebx esi
75
        stdcall get_service, ebx
76
        pop     esi ebx
77
        test    eax, eax
78
        jz      .driver_fail
79
        mov     edx, [eax+USBSRV.usb_func]
80
        cmp     [edx+usb_hardware_func.Version], USBHC_VERSION
81
        jnz     .driver_invalid
82
        mov     [esi+PCIDEV.owner], eax
83
        call    [edx+usb_hardware_func.BeforeInit]
3520 clevermous 84
        jmp     .kickoff
4418 clevermous 85
.driver_fail:
86
        DEBUGF 1,'K : failed to load driver %s\n',ebx
87
        jmp     .kickoff
88
.driver_invalid:
89
        DEBUGF 1,'K : driver %s has wrong version\n',ebx
90
        jmp     .kickoff
3520 clevermous 91
.done_kickoff:
92
        pop     eax
93
; 3. If no controllers were found, exit.
94
; Otherwise, run the USB thread.
95
        test    eax, eax
96
        jz      .nothing
97
        call    create_usb_thread
98
        jz      .nothing
99
; 4. Initialize all USB controllers, calling usb_init_controller for each.
100
; Note: USB1 companion(s) should go before the corresponding EHCI controller,
101
; although this is not strictly necessary (this way, a companion would not try
102
; to initialize high-speed device only to see a disconnect when EHCI takes
103
; control).
104
; Thus, process all EHCI controllers in the first loop, all USB1 controllers
105
; in the second loop. (One loop in reversed PCI order could also be used,
106
; but seems less natural.)
107
; 4a. Loop over all PCI devices, call usb_init_controller
108
; for all EHCI controllers.
109
        mov     eax, pcidev_list
110
.scan_ehci:
111
        mov     eax, [eax+PCIDEV.fd]
112
        cmp     eax, pcidev_list
113
        jz      .done_ehci
114
        cmp     [eax+PCIDEV.class], 0x0C0320
115
        jnz     .scan_ehci
116
        call    usb_init_controller
117
        jmp     .scan_ehci
118
.done_ehci:
119
; 4b. Loop over all PCI devices, call usb_init_controller
120
; for all UHCI and OHCI controllers.
121
        mov     eax, pcidev_list
122
.scan_usb1:
123
        mov     eax, [eax+PCIDEV.fd]
124
        cmp     eax, pcidev_list
125
        jz      .done_usb1
126
        cmp     [eax+PCIDEV.class], 0x0C0300
127
        jz      @f
128
        cmp     [eax+PCIDEV.class], 0x0C0310
129
        jnz     .scan_usb1
130
@@:
131
        call    usb_init_controller
132
        jmp     .scan_usb1
133
.done_usb1:
134
.nothing:
135
        ret
136
endp
137
 
138
uglobal
139
align 4
140
usb_event       dd      ?
141
endg
142
 
143
; Helper function for usb_init. Creates and initializes the USB thread.
144
proc create_usb_thread
145
; 1. Create the thread.
146
        push    edi
3598 clevermous 147
        movi    ebx, 1
3520 clevermous 148
        mov     ecx, usb_thread_proc
149
        xor     edx, edx
150
        call    new_sys_threads
151
        pop     edi
152
; If failed, say something to the debug board and return with ZF set.
153
        test    eax, eax
154
        jns     @f
155
        DEBUGF 1,'K : cannot create kernel thread for USB, error %d\n',eax
156
.clear:
157
        xor     eax, eax
158
        jmp     .nothing
159
@@:
160
; 2. Wait while the USB thread initializes itself.
161
@@:
162
        call    change_task
163
        cmp     [usb_event], 0
164
        jz      @b
165
; 3. If initialization failed, the USB thread sets [usb_event] to -1.
166
; Return with ZF set or cleared corresponding to the result.
167
        cmp     [usb_event], -1
168
        jz      .clear
169
.nothing:
170
        ret
171
endp
172
 
173
; Helper function for IRQ handlers. Wakes the USB thread if ebx is nonzero.
174
proc usb_wakeup_if_needed
175
        test    ebx, ebx
176
        jz      usb_wakeup.nothing
177
usb_wakeup:
178
        xor     edx, edx
179
        mov     eax, [usb_event]
180
        mov     ebx, [eax+EVENT.id]
181
        xor     esi, esi
182
        call    raise_event
183
.nothing:
184
        ret
185
endp
186
 
187
; Main loop of the USB thread.
188
proc usb_thread_proc
189
; 1. Initialize: create event to allow wakeup by interrupt handlers.
190
        xor     esi, esi
191
        mov     ecx, MANUAL_DESTROY
192
        call    create_event
193
        test    eax, eax
194
        jnz     @f
195
; If failed, set [usb_event] to -1 and terminate myself.
196
        dbgstr 'cannot create event for USB thread'
197
        or      [usb_event], -1
198
        jmp     sys_end
199
@@:
200
        mov     [usb_event], eax
201
        push    -1              ; initial timeout: infinite
202
usb_thread_wait:
203
; 2. Main loop: wait for either wakeup event or timeout.
204
        pop     ecx             ; get timeout
205
        mov     eax, [usb_event]
206
        mov     ebx, [eax+EVENT.id]
207
        call    wait_event_timeout
208
        push    -1              ; default timeout: infinite
209
; 3. Main loop: call worker functions of all controllers;
210
; if some function schedules wakeup in timeout less than the current value,
211
; replace that value with the returned timeout.
212
        mov     esi, usb_controllers_list
213
@@:
214
        mov     esi, [esi+usb_controller.Next]
215
        cmp     esi, usb_controllers_list
216
        jz      .controllers_done
217
        mov     eax, [esi+usb_controller.HardwareFunc]
218
        call    [eax+usb_hardware_func.ProcessDeferred]
219
        cmp     [esp], eax
220
        jb      @b
221
        mov     [esp], eax
222
        jmp     @b
223
.controllers_done:
224
; 4. Main loop: call hub worker function for all hubs,
225
; similarly calculating minimum of all returned timeouts.
226
; When done, continue to 2.
227
        mov     esi, usb_hubs_list
228
@@:
229
        mov     esi, [esi+usb_hub.Next]
230
        cmp     esi, usb_hubs_list
231
        jz      usb_thread_wait
232
        call    usb_hub_process_deferred
233
        cmp     [esp], eax
234
        jb      @b
235
        mov     [esp], eax
236
        jmp     @b
237
endp
238
 
239
iglobal
240
align 4
241
usb_controllers_list:
242
        dd      usb_controllers_list
243
        dd      usb_controllers_list
244
usb_hubs_list:
245
        dd      usb_hubs_list
246
        dd      usb_hubs_list
247
endg
248
uglobal
249
align 4
250
usb_controllers_list_mutex      MUTEX
251
endg
252
 
253
include "memory.inc"
4418 clevermous 254
include "common.inc"
3520 clevermous 255
include "hccommon.inc"
256
include "pipe.inc"
257
include "protocol.inc"
258
include "hub.inc"