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