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" |