Rev 5363 | 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: 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" |