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