Rev 4300 | Rev 4547 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3520 | clevermous | 1 | ; Code for EHCI controllers. |
2 | |||
4418 | clevermous | 3 | ; Standard driver stuff |
4 | format PE DLL native |
||
5 | entry start |
||
6 | __DEBUG__ equ 1 |
||
7 | __DEBUG_LEVEL__ equ 1 |
||
8 | section '.reloc' data readable discardable fixups |
||
9 | section '.text' code readable executable |
||
10 | include '../proc32.inc' |
||
11 | include '../struct.inc' |
||
12 | include '../macros.inc' |
||
13 | include '../fdo.inc' |
||
14 | include '../../kernel/trunk/bus/usb/common.inc' |
||
15 | |||
3520 | clevermous | 16 | ; ============================================================================= |
17 | ; ================================= Constants ================================= |
||
18 | ; ============================================================================= |
||
19 | ; EHCI register declarations. |
||
20 | ; Part 1. Capability registers. |
||
21 | ; Base is MMIO from the PCI space. |
||
22 | EhciCapLengthReg = 0 |
||
23 | EhciVersionReg = 2 |
||
24 | EhciStructParamsReg = 4 |
||
25 | EhciCapParamsReg = 8 |
||
26 | EhciPortRouteReg = 0Ch |
||
27 | ; Part 2. Operational registers. |
||
28 | ; Base is (base for part 1) + (value of EhciCapLengthReg). |
||
29 | EhciCommandReg = 0 |
||
30 | EhciStatusReg = 4 |
||
31 | EhciInterruptReg = 8 |
||
32 | EhciFrameIndexReg = 0Ch |
||
33 | EhciCtrlDataSegReg = 10h |
||
34 | EhciPeriodicListReg = 14h |
||
35 | EhciAsyncListReg = 18h |
||
36 | EhciConfigFlagReg = 40h |
||
37 | EhciPortsReg = 44h |
||
38 | |||
39 | ; Possible values of ehci_pipe.NextQH.Type bitfield. |
||
40 | EHCI_TYPE_ITD = 0 ; isochronous transfer descriptor |
||
41 | EHCI_TYPE_QH = 1 ; queue head |
||
42 | EHCI_TYPE_SITD = 2 ; split-transaction isochronous TD |
||
43 | EHCI_TYPE_FSTN = 3 ; frame span traversal node |
||
44 | |||
45 | ; ============================================================================= |
||
46 | ; ================================ Structures ================================= |
||
47 | ; ============================================================================= |
||
48 | |||
49 | ; Hardware part of EHCI general transfer descriptor. |
||
50 | struct ehci_hardware_td |
||
51 | NextTD dd ? |
||
52 | ; Bit 0 is Terminate bit, 1 = there is no next TD. |
||
53 | ; Bits 1-4 must be zero. |
||
54 | ; With masked 5 lower bits, this is the physical address of the next TD, if any. |
||
55 | AlternateNextTD dd ? |
||
56 | ; Similar to NextTD, used if the transfer terminates with a short packet. |
||
57 | Token dd ? |
||
58 | ; 1. Lower byte is Status field: |
||
59 | ; bit 0 = ping state for USB2 endpoints, ERR handshake signal for USB1 endpoints |
||
60 | ; bit 1 = split transaction state, meaningless for USB2 endpoints |
||
61 | ; bit 2 = missed micro-frame |
||
62 | ; bit 3 = transaction error |
||
63 | ; bit 4 = babble detected |
||
64 | ; bit 5 = data buffer error |
||
65 | ; bit 6 = halted |
||
66 | ; bit 7 = active |
||
67 | ; 2. Next two bits (bits 8-9) are PID code, 0 = OUT, 1 = IN, 2 = SETUP. |
||
68 | ; 3. Next two bits (bits 10-11) is ErrorCounter. Initialized as 3, decremented |
||
69 | ; on each error; if it goes to zero, transaction is stopped. |
||
70 | ; 4. Next 3 bits (bits 12-14) are CurrentPage field. |
||
71 | ; 5. Next bit (bit 15) is InterruptOnComplete bit. |
||
72 | ; 6. Next 15 bits (bits 16-30) are TransferLength field, |
||
73 | ; number of bytes to transfer. |
||
74 | ; 7. Upper bit (bit 31) is DataToggle bit. |
||
75 | BufferPointers rd 5 |
||
76 | ; The buffer to be transferred can be spanned on up to 5 physical pages. |
||
77 | ; The first item of this array is the physical address of the first byte in |
||
78 | ; the buffer, other items are physical addresses of next pages. Lower 12 bits |
||
79 | ; in other items must be set to zero; ehci_pipe.Overlay reuses some of them. |
||
80 | BufferPointersHigh rd 5 |
||
81 | ; Upper dwords of BufferPointers for controllers with 64-bit memory access. |
||
82 | ; Always zero. |
||
83 | ends |
||
84 | |||
85 | ; EHCI general transfer descriptor. |
||
86 | ; * The structure describes transfers to be performed on Control, Bulk or |
||
87 | ; Interrupt endpoints. |
||
88 | ; * The structure includes two parts, the hardware part and the software part. |
||
89 | ; * The hardware part consists of first 52 bytes and corresponds to |
||
90 | ; the Queue Element Transfer Descriptor from EHCI specification. |
||
91 | ; * The hardware requires 32-bytes alignment of the hardware part, so |
||
92 | ; the entire descriptor must be 32-bytes aligned. Since the allocator |
||
93 | ; (usb_allocate_common) allocates memory sequentially from page start |
||
3653 | clevermous | 94 | ; (aligned on 0x1000 bytes), block size for the allocator must be divisible |
95 | ; by 32; ehci_alloc_td ensures this. |
||
3520 | clevermous | 96 | ; * The hardware also requires that the hardware part must not cross page |
97 | ; boundary; the allocator satisfies this automatically. |
||
98 | struct ehci_gtd ehci_hardware_td |
||
99 | Flags dd ? |
||
100 | ; Copy of flags from the call to usb_*_transfer_async. |
||
101 | ends |
||
102 | |||
103 | ; EHCI-specific part of a pipe descriptor. |
||
104 | ; * This structure corresponds to the Queue Head from the EHCI specification. |
||
105 | ; * The hardware requires 32-bytes alignment of the hardware part. |
||
106 | ; Since the allocator (usb_allocate_common) allocates memory sequentially |
||
3653 | clevermous | 107 | ; from page start (aligned on 0x1000 bytes), block size for the allocator |
108 | ; must be divisible by 32; ehci_alloc_pipe ensures this. |
||
3520 | clevermous | 109 | ; * The hardware requires also that the hardware part must not cross page |
110 | ; boundary; the allocator satisfies this automatically. |
||
111 | struct ehci_pipe |
||
112 | NextQH dd ? |
||
113 | ; 1. First bit (bit 0) is Terminate bit, 1 = there is no next QH. |
||
114 | ; 2. Next two bits (bits 1-2) are Type field of the next QH, |
||
115 | ; one of EHCI_TYPE_* constants. |
||
116 | ; 3. Next two bits (bits 3-4) are reserved, must be zero. |
||
117 | ; 4. With masked 5 lower bits, this is the physical address of the next object |
||
118 | ; to be processed, usually next QH. |
||
119 | Token dd ? |
||
120 | ; 1. Lower 7 bits are DeviceAddress field. This is the address of the |
||
121 | ; target device on the USB bus. |
||
122 | ; 2. Next bit (bit 7) is Inactivate-on-next-transaction bit. Can be nonzero |
||
123 | ; only for interrupt/isochronous USB1 endpoints. |
||
124 | ; 3. Next 4 bits (bits 8-11) are Endpoint field. This is the target endpoint |
||
125 | ; number. |
||
126 | ; 4. Next 2 bits (bits 12-13) are EndpointSpeed field, one of EHCI_SPEED_*. |
||
127 | ; 5. Next bit (bit 14) is DataToggleControl bit, |
||
128 | ; 0 = use DataToggle bit from QH, 1 = from TD. |
||
129 | ; 6. Next bit (bit 15) is Head-of-reclamation-list. The head of Control list |
||
130 | ; has 1 here, all other QHs have zero. |
||
131 | ; 7. Next 11 bits (bits 16-26) are MaximumPacketLength field for the target |
||
132 | ; endpoint. |
||
133 | ; 8. Next bit (bit 27) is ControlEndpoint bit, must be 1 for USB1 control |
||
134 | ; endpoints and 0 for all others. |
||
135 | ; 9. Upper 4 bits (bits 28-31) are NakCountReload field. |
||
136 | ; Zero for USB1 endpoints, zero for periodic endpoints. |
||
137 | ; For control/bulk USB2 endpoints, the code sets it to 4, |
||
138 | ; which is rather arbitrary. |
||
139 | Flags dd ? |
||
140 | ; 1. Lower byte is S-mask, each bit corresponds to one microframe per frame; |
||
141 | ; bit is set <=> enable transactions in this microframe. |
||
142 | ; 2. Next byte is C-mask, each bit corresponds to one microframe per frame; |
||
143 | ; bit is set <=> enable complete-split transactions in this microframe. |
||
144 | ; Meaningful only for USB1 endpoints. |
||
145 | ; 3. Next 14 bits give address of the target device as hub:port, bits 16-22 |
||
146 | ; are the USB address of the hub, bits 23-29 are the port number. |
||
147 | ; Meaningful only for USB1 endpoints. |
||
148 | ; 4. Upper 2 bits define number of consequetive transactions per micro-frame |
||
149 | ; which host is allowed to permit for this endpoint. |
||
150 | ; For control/bulk endpoints, it must be 1. |
||
151 | ; For periodic endpoints, the value is taken from the endpoint descriptor. |
||
152 | HeadTD dd ? |
||
153 | ; The physical address of the first TD for this pipe. |
||
154 | ; Lower 5 bits must be zero. |
||
155 | Overlay ehci_hardware_td ? |
||
156 | ; Working area for the current TD, if there is any. |
||
157 | ; When TD is retired, it is written to that TD and Overlay is loaded |
||
158 | ; from the new TD, if any. |
||
159 | BaseList dd ? |
||
160 | ; Pointer to head of the corresponding pipe list. |
||
161 | ends |
||
162 | |||
163 | ; This structure describes the static head of every list of pipes. |
||
164 | ; The hardware requires 32-bytes alignment of this structure. |
||
165 | ; All instances of this structure are located sequentially in ehci_controller, |
||
166 | ; ehci_controller is page-aligned, so it is sufficient to make this structure |
||
167 | ; 32-bytes aligned and verify that the first instance is 32-bytes aligned |
||
168 | ; inside ehci_controller. |
||
169 | ; The hardware also requires that 44h bytes (size of 64-bit Queue Head |
||
170 | ; Descriptor) starting at the beginning of this structure must not cross page |
||
171 | ; boundary. If not, most hardware still behaves correctly (in fact, the last |
||
172 | ; dword can have any value and this structure is never written), but on some |
||
173 | ; hardware some things just break in mysterious ways. |
||
174 | struct ehci_static_ep |
||
175 | ; Hardware fields are the same as in ehci_pipe. |
||
176 | ; Only NextQH and Overlay.Token are actually used. |
||
177 | ; NB: some emulators ignore Token.Halted bit (probably assuming that it is set |
||
178 | ; only when device fails and emulation never fails) and always follow |
||
179 | ; [Alternate]NextTD when they see that OverlayToken.Active bit is zero; |
||
180 | ; so it is important to also set [Alternate]NextTD to 1. |
||
181 | NextQH dd ? |
||
182 | Token dd ? |
||
183 | Flags dd ? |
||
184 | HeadTD dd ? |
||
185 | NextTD dd ? |
||
186 | AlternateNextTD dd ? |
||
187 | OverlayToken dd ? |
||
188 | NextList dd ? |
||
189 | SoftwarePart rd sizeof.usb_static_ep/4 |
||
190 | Bandwidths rw 8 |
||
191 | dd ? |
||
192 | ends |
||
193 | |||
194 | if sizeof.ehci_static_ep mod 32 |
||
195 | .err ehci_static_ep must be 32-bytes aligned |
||
196 | end if |
||
197 | |||
198 | if ehci_static_ep.OverlayToken <> ehci_pipe.Overlay.Token |
||
199 | .err ehci_static_ep.OverlayToken misplaced |
||
200 | end if |
||
201 | |||
202 | ; EHCI-specific part of controller data. |
||
203 | ; * The structure includes two parts, the hardware part and the software part. |
||
204 | ; * The hardware part consists of first 4096 bytes and corresponds to |
||
205 | ; the Periodic Frame List from the EHCI specification. |
||
206 | ; * The hardware requires page-alignment of the hardware part, so |
||
207 | ; the entire descriptor must be page-aligned. |
||
208 | ; This structure is allocated with kernel_alloc (see usb_init_controller), |
||
209 | ; this gives page-aligned data. |
||
210 | ; * The controller is described by both ehci_controller and usb_controller |
||
211 | ; structures, for each controller there is one ehci_controller and one |
||
212 | ; usb_controller structure. These structures are located sequentially |
||
213 | ; in the memory: beginning from some page start, there is ehci_controller |
||
214 | ; structure - this enforces hardware alignment requirements - and then |
||
215 | ; usb_controller structure. |
||
216 | ; * The code keeps pointer to usb_controller structure. The ehci_controller |
||
217 | ; structure is addressed as [ptr + ehci_controller.field - sizeof.ehci_controller]. |
||
218 | struct ehci_controller |
||
219 | ; ------------------------------ hardware fields ------------------------------ |
||
220 | FrameList rd 1024 |
||
221 | ; Entry n corresponds to the head of the frame list to be executed in |
||
3578 | clevermous | 222 | ; the frames n,n+1024,n+2048,n+3072,... |
3520 | clevermous | 223 | ; The first bit of each entry is Terminate bit, 1 = the frame is empty. |
224 | ; Bits 1-2 are Type field, one of EHCI_TYPE_* constants. |
||
225 | ; Bits 3-4 must be zero. |
||
226 | ; With masked 5 lower bits, the entry is a physical address of the first QH/TD |
||
227 | ; to be executed. |
||
228 | ; ------------------------------ software fields ------------------------------ |
||
229 | ; Every list has the static head, which is an always halted QH. |
||
230 | ; The following fields are static heads, one per list: |
||
231 | ; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list. |
||
232 | IntEDs ehci_static_ep |
||
233 | rb 62 * sizeof.ehci_static_ep |
||
234 | ; Beware. |
||
235 | ; Two following strings ensure that 44h bytes at any static head |
||
236 | ; do not cross page boundary. Without that, the code "works on my machine"... |
||
237 | ; but fails on some hardware in seemingly unrelated ways. |
||
238 | ; One hardware TD (without any software fields) fit in the rest of the page. |
||
239 | ehci_controller.ControlDelta = 2000h - (ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) |
||
240 | StopQueueTD ehci_hardware_td |
||
241 | ; Used as AlternateNextTD for transfers when short packet is considered |
||
242 | ; as an error; short packet must stop the queue in this case, not advance |
||
243 | ; to the next transfer. |
||
244 | rb ehci_controller.ControlDelta - sizeof.ehci_hardware_td |
||
245 | ; Padding for page-alignment. |
||
246 | ControlED ehci_static_ep |
||
247 | BulkED ehci_static_ep |
||
248 | MMIOBase1 dd ? |
||
249 | ; Virtual address of memory-mapped area with part 1 of EHCI registers EhciXxxReg. |
||
250 | MMIOBase2 dd ? |
||
251 | ; Pointer inside memory-mapped area MMIOBase1; points to part 2 of EHCI registers. |
||
252 | StructuralParams dd ? |
||
253 | ; Copy of EhciStructParamsReg value. |
||
254 | CapabilityParams dd ? |
||
255 | ; Copy of EhciCapParamsReg value. |
||
256 | DeferredActions dd ? |
||
257 | ; Bitmask of events from EhciStatusReg which were observed by the IRQ handler |
||
258 | ; and needs to be processed in the IRQ thread. |
||
4418 | clevermous | 259 | PortRoutes rb 16 |
260 | ; Companion port route description. |
||
261 | ; Each byte describes routing of one port, value = PCI function. |
||
262 | ; This field must be the last one: |
||
263 | ; UHCI/OHCI code uses this field without knowing the entire structure. |
||
3520 | clevermous | 264 | ends |
265 | |||
266 | if ehci_controller.IntEDs mod 32 |
||
267 | .err Static endpoint descriptors must be 32-bytes aligned inside ehci_controller |
||
268 | end if |
||
269 | |||
270 | ; Description of #HCI-specific data and functions for |
||
271 | ; controller-independent code. |
||
272 | ; Implements the structure usb_hardware_func from hccommon.inc for EHCI. |
||
273 | iglobal |
||
274 | align 4 |
||
275 | ehci_hardware_func: |
||
4418 | clevermous | 276 | dd USBHC_VERSION |
3520 | clevermous | 277 | dd 'EHCI' |
278 | dd sizeof.ehci_controller |
||
4418 | clevermous | 279 | dd ehci_kickoff_bios |
3520 | clevermous | 280 | dd ehci_init |
281 | dd ehci_process_deferred |
||
282 | dd ehci_set_device_address |
||
283 | dd ehci_get_device_address |
||
284 | dd ehci_port_disable |
||
285 | dd ehci_new_port.reset |
||
286 | dd ehci_set_endpoint_packet_size |
||
287 | dd ehci_alloc_pipe |
||
288 | dd ehci_free_pipe |
||
289 | dd ehci_init_pipe |
||
290 | dd ehci_unlink_pipe |
||
291 | dd ehci_alloc_td |
||
292 | dd ehci_free_td |
||
293 | dd ehci_alloc_transfer |
||
294 | dd ehci_insert_transfer |
||
295 | dd ehci_new_device |
||
4418 | clevermous | 296 | ehci_name db 'EHCI',0 |
3520 | clevermous | 297 | endg |
298 | |||
299 | ; ============================================================================= |
||
300 | ; =================================== Code ==================================== |
||
301 | ; ============================================================================= |
||
302 | |||
4418 | clevermous | 303 | ; Called once when driver is loading and once at shutdown. |
304 | ; When loading, must initialize itself, register itself in the system |
||
305 | ; and return eax = value obtained when registering. |
||
306 | proc start |
||
307 | virtual at esp |
||
308 | dd ? ; return address |
||
309 | .reason dd ? ; DRV_ENTRY or DRV_EXIT |
||
310 | .cmdline dd ? ; normally NULL |
||
311 | end virtual |
||
312 | cmp [.reason], DRV_ENTRY |
||
313 | jnz .nothing |
||
314 | mov ecx, ehci_ep_mutex |
||
315 | invoke MutexInit |
||
316 | mov ecx, ehci_gtd_mutex |
||
317 | invoke MutexInit |
||
318 | push esi edi |
||
319 | mov esi, [USBHCFunc] |
||
320 | mov edi, usbhc_api |
||
321 | movi ecx, sizeof.usbhc_func/4 |
||
322 | rep movsd |
||
323 | pop edi esi |
||
324 | invoke RegUSBDriver, ehci_name, 0, ehci_hardware_func |
||
325 | .nothing: |
||
326 | ret |
||
327 | endp |
||
328 | |||
3520 | clevermous | 329 | ; Controller-specific initialization function. |
330 | ; Called from usb_init_controller. Initializes the hardware and |
||
331 | ; EHCI-specific parts of software structures. |
||
332 | ; eax = pointer to ehci_controller to be initialized |
||
333 | ; [ebp-4] = pcidevice |
||
334 | proc ehci_init |
||
335 | ; inherit some variables from the parent (usb_init_controller) |
||
336 | .devfn equ ebp - 4 |
||
337 | .bus equ ebp - 3 |
||
338 | ; 1. Store pointer to ehci_controller for further use. |
||
339 | push eax |
||
340 | mov edi, eax |
||
341 | mov esi, eax |
||
342 | ; 2. Initialize ehci_controller.FrameList. |
||
343 | ; Note that FrameList is located in the beginning of ehci_controller, |
||
344 | ; so esi and edi now point to ehci_controller.FrameList. |
||
345 | ; First 32 entries of FrameList contain physical addresses |
||
346 | ; of first 32 Periodic static heads, further entries duplicate these. |
||
347 | ; See the description of structures for full info. |
||
348 | ; 2a. Get physical address of first static head. |
||
349 | ; Note that 1) it is located in the beginning of a page |
||
350 | ; and 2) first 32 static heads fit in the same page, |
||
351 | ; so one call to get_phys_addr without correction of lower 12 bits |
||
352 | ; is sufficient. |
||
353 | if (ehci_controller.IntEDs / 0x1000) <> ((ehci_controller.IntEDs + 32 * sizeof.ehci_static_ep) / 0x1000) |
||
354 | .err assertion failed |
||
355 | end if |
||
356 | if (ehci_controller.IntEDs mod 0x1000) <> 0 |
||
357 | .err assertion failed |
||
358 | end if |
||
359 | add eax, ehci_controller.IntEDs |
||
4418 | clevermous | 360 | call [GetPhysAddr] |
3520 | clevermous | 361 | ; 2b. Fill first 32 entries. |
362 | inc eax |
||
363 | inc eax ; set Type to EHCI_TYPE_QH |
||
3598 | clevermous | 364 | movi ecx, 32 |
3520 | clevermous | 365 | mov edx, ecx |
366 | @@: |
||
367 | stosd |
||
368 | add eax, sizeof.ehci_static_ep |
||
369 | loop @b |
||
370 | ; 2c. Fill the rest entries. |
||
371 | mov ecx, 1024 - 32 |
||
372 | rep movsd |
||
373 | ; 3. Initialize static heads ehci_controller.*ED. |
||
374 | ; Use the loop over groups: first group consists of first 32 Periodic |
||
375 | ; descriptors, next group consists of next 16 Periodic descriptors, |
||
376 | ; ..., last group consists of the last Periodic descriptor. |
||
377 | ; 3a. Prepare for the loop. |
||
378 | ; make esi point to the second group, other registers are already set. |
||
379 | add esi, 32*4 + 32*sizeof.ehci_static_ep |
||
380 | ; 3b. Loop over groups. On every iteration: |
||
381 | ; edx = size of group, edi = pointer to the current group, |
||
382 | ; esi = pointer to the next group. |
||
383 | .init_static_eds: |
||
384 | ; 3c. Get the size of next group. |
||
385 | shr edx, 1 |
||
386 | ; 3d. Exit the loop if there is no next group. |
||
387 | jz .init_static_eds_done |
||
388 | ; 3e. Initialize the first half of the current group. |
||
389 | ; Advance edi to the second half. |
||
390 | push esi |
||
391 | call ehci_init_static_ep_group |
||
392 | pop esi |
||
393 | ; 3f. Initialize the second half of the current group |
||
394 | ; with the same values. |
||
395 | ; Advance edi to the next group, esi/eax to the next of the next group. |
||
396 | call ehci_init_static_ep_group |
||
397 | jmp .init_static_eds |
||
398 | .init_static_eds_done: |
||
399 | ; 3g. Initialize the last static head. |
||
400 | xor esi, esi |
||
401 | call ehci_init_static_endpoint |
||
402 | ; While we are here, initialize StopQueueTD. |
||
403 | if (ehci_controller.StopQueueTD <> ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) |
||
404 | .err assertion failed |
||
405 | end if |
||
406 | inc [edi+ehci_hardware_td.NextTD] ; 0 -> 1 |
||
407 | inc [edi+ehci_hardware_td.AlternateNextTD] ; 0 -> 1 |
||
408 | ; leave other fields as zero, including Active bit |
||
409 | ; 3i. Initialize the head of Control list. |
||
410 | add edi, ehci_controller.ControlDelta |
||
411 | lea esi, [edi+sizeof.ehci_static_ep] |
||
412 | call ehci_init_static_endpoint |
||
413 | or byte [edi-sizeof.ehci_static_ep+ehci_static_ep.Token+1], 80h |
||
414 | ; 3j. Initialize the head of Bulk list. |
||
415 | sub esi, sizeof.ehci_static_ep |
||
416 | call ehci_init_static_endpoint |
||
417 | ; 4. Create a virtual memory area to talk with the controller. |
||
418 | ; 4a. Enable memory & bus master access. |
||
4418 | clevermous | 419 | invoke PciRead16, dword [.bus], dword [.devfn], 4 |
3520 | clevermous | 420 | or al, 6 |
4418 | clevermous | 421 | invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax |
3520 | clevermous | 422 | ; 4b. Read memory base address. |
4418 | clevermous | 423 | invoke PciRead32, dword [.bus], dword [.devfn], 10h |
3520 | clevermous | 424 | ; DEBUGF 1,'K : phys MMIO %x\n',eax |
425 | and al, not 0Fh |
||
426 | ; 4c. Create mapping for physical memory. 200h bytes are always sufficient. |
||
4418 | clevermous | 427 | invoke MapIoMem, eax, 200h, PG_SW+PG_NOCACHE |
3520 | clevermous | 428 | test eax, eax |
429 | jz .fail |
||
430 | ; DEBUGF 1,'K : MMIO %x\n',eax |
||
431 | if ehci_controller.MMIOBase1 <> ehci_controller.BulkED + sizeof.ehci_static_ep |
||
432 | .err assertion failed |
||
433 | end if |
||
434 | stosd ; fill ehci_controller.MMIOBase1 |
||
4418 | clevermous | 435 | ; 5. Read basic parameters of the controller. |
436 | ; 5a. Structural parameters. |
||
437 | mov ebx, [eax+EhciStructParamsReg] |
||
438 | ; 5b. Port routing rules. |
||
439 | ; If bit 7 in HCSPARAMS is set, read and unpack EhciPortRouteReg. |
||
440 | ; Otherwise, bits 11:8 are N_PCC = number of ports per companion, |
||
441 | ; bits 15:12 are number of companions, maybe zero, |
||
442 | ; first N_PCC ports are routed to the first companion and so on. |
||
443 | xor esi, esi |
||
444 | test bl, bl |
||
445 | js .read_routes |
||
446 | test bh, 0x0F |
||
447 | jz .no_companions |
||
448 | test bh, 0xF0 |
||
449 | jz .no_companions |
||
450 | xor edx, edx |
||
451 | .fill_routes: |
||
452 | movzx ecx, bh |
||
453 | and ecx, 15 |
||
454 | @@: |
||
455 | mov byte [edi+esi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)], dl |
||
456 | inc esi |
||
457 | cmp esi, 16 |
||
458 | jz .routes_filled |
||
459 | dec ecx |
||
460 | jnz @b |
||
461 | movzx ecx, bh |
||
462 | shr ecx, 4 |
||
463 | inc edx |
||
464 | cmp edx, ecx |
||
465 | jb .fill_routes |
||
466 | .no_companions: |
||
467 | mov byte [edi+esi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)], 0xFF |
||
468 | inc esi |
||
469 | cmp esi, 16 |
||
470 | jnz .no_companions |
||
471 | jmp .routes_filled |
||
472 | .read_routes: |
||
473 | rept 2 counter |
||
474 | { |
||
475 | mov ecx, [eax+EhciPortRouteReg+(counter-1)*4] |
||
476 | @@: |
||
477 | mov edx, ecx |
||
478 | shr ecx, 4 |
||
479 | and edx, 15 |
||
480 | mov byte [edi+esi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)], dl |
||
481 | inc esi |
||
482 | cmp esi, 8*counter |
||
483 | jnz @b |
||
484 | } |
||
485 | .routes_filled: |
||
486 | ; DEBUGF 1,'K : EhciPortRouteReg: %x %x\n',[eax+EhciPortRouteReg],[eax+EhciPortRouteReg+4] |
||
487 | ; DEBUGF 1,'K : routes:\nK : ' |
||
488 | ;rept 8 counter |
||
489 | ;{ |
||
490 | ; DEBUGF 1,' %x',[edi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)+counter-1]:2 |
||
491 | ;} |
||
492 | ; DEBUGF 1,'\nK : ' |
||
493 | ;rept 8 counter |
||
494 | ;{ |
||
495 | ; DEBUGF 1,' %x',[edi+ehci_controller.PortRoutes+8-(ehci_controller.MMIOBase1+4)+counter-1]:2 |
||
496 | ;} |
||
497 | ; DEBUGF 1,'\n' |
||
3520 | clevermous | 498 | movzx ecx, byte [eax+EhciCapLengthReg] |
499 | mov edx, [eax+EhciCapParamsReg] |
||
500 | add eax, ecx |
||
501 | if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4 |
||
502 | .err assertion failed |
||
503 | end if |
||
504 | stosd ; fill ehci_controller.MMIOBase2 |
||
505 | if ehci_controller.StructuralParams <> ehci_controller.MMIOBase2 + 4 |
||
506 | .err assertion failed |
||
507 | end if |
||
508 | if ehci_controller.CapabilityParams <> ehci_controller.StructuralParams + 4 |
||
509 | .err assertion failed |
||
510 | end if |
||
511 | mov [edi], ebx ; fill ehci_controller.StructuralParams |
||
512 | mov [edi+4], edx ; fill ehci_controller.CapabilityParams |
||
513 | DEBUGF 1,'K : HCSPARAMS=%x, HCCPARAMS=%x\n',ebx,edx |
||
514 | and ebx, 15 |
||
515 | mov [edi+usb_controller.NumPorts+sizeof.ehci_controller-ehci_controller.StructuralParams], ebx |
||
516 | mov edi, eax |
||
517 | ; now edi = MMIOBase2 |
||
518 | ; 6. Transfer the controller to a known state. |
||
519 | ; 6b. Stop the controller if it is running. |
||
3598 | clevermous | 520 | movi ecx, 10 |
3520 | clevermous | 521 | test dword [edi+EhciStatusReg], 1 shl 12 |
522 | jnz .stopped |
||
523 | and dword [edi+EhciCommandReg], not 1 |
||
524 | @@: |
||
3598 | clevermous | 525 | movi esi, 1 |
4418 | clevermous | 526 | invoke Sleep |
3520 | clevermous | 527 | test dword [edi+EhciStatusReg], 1 shl 12 |
528 | jnz .stopped |
||
529 | loop @b |
||
530 | dbgstr 'Failed to stop EHCI controller' |
||
531 | jmp .fail_unmap |
||
532 | .stopped: |
||
533 | ; 6c. Reset the controller. Wait up to 50 ms checking status every 1 ms. |
||
534 | or dword [edi+EhciCommandReg], 2 |
||
3598 | clevermous | 535 | movi ecx, 50 |
3520 | clevermous | 536 | @@: |
3598 | clevermous | 537 | movi esi, 1 |
4418 | clevermous | 538 | invoke Sleep |
3520 | clevermous | 539 | test dword [edi+EhciCommandReg], 2 |
540 | jz .reset_ok |
||
541 | loop @b |
||
542 | dbgstr 'Failed to reset EHCI controller' |
||
543 | jmp .fail_unmap |
||
544 | .reset_ok: |
||
545 | ; 7. Configure the controller. |
||
546 | pop esi ; restore the pointer saved at step 1 |
||
547 | add esi, sizeof.ehci_controller |
||
548 | ; 7a. If the controller is 64-bit, say to it that all structures are located |
||
549 | ; in first 4G. |
||
550 | test byte [esi+ehci_controller.CapabilityParams-sizeof.ehci_controller], 1 |
||
551 | jz @f |
||
552 | mov dword [edi+EhciCtrlDataSegReg], 0 |
||
553 | @@: |
||
554 | ; 7b. Hook interrupt and enable appropriate interrupt sources. |
||
4418 | clevermous | 555 | invoke PciRead8, dword [.bus], dword [.devfn], 3Ch |
3520 | clevermous | 556 | ; al = IRQ |
4418 | clevermous | 557 | ; DEBUGF 1,'K : attaching to IRQ %x\n',al |
3520 | clevermous | 558 | movzx eax, al |
4418 | clevermous | 559 | invoke AttachIntHandler, eax, ehci_irq, esi |
3520 | clevermous | 560 | ; mov dword [edi+EhciStatusReg], 111111b ; clear status |
561 | ; disable Frame List Rollover interrupt, enable all other sources |
||
562 | mov dword [edi+EhciInterruptReg], 110111b |
||
563 | ; 7c. Inform the controller of the address of periodic lists head. |
||
564 | lea eax, [esi-sizeof.ehci_controller] |
||
4418 | clevermous | 565 | invoke GetPhysAddr |
3520 | clevermous | 566 | mov dword [edi+EhciPeriodicListReg], eax |
567 | ; 7d. Inform the controller of the address of asynchronous lists head. |
||
568 | lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller] |
||
4418 | clevermous | 569 | invoke GetPhysAddr |
3520 | clevermous | 570 | mov dword [edi+EhciAsyncListReg], eax |
571 | ; 7e. Configure operational details and run the controller. |
||
572 | mov dword [edi+EhciCommandReg], \ |
||
573 | (1 shl 16) + \ ; interrupt threshold = 1 microframe = 0.125ms |
||
574 | (0 shl 11) + \ ; disable Async Park Mode |
||
575 | (0 shl 8) + \ ; zero Async Park Mode Count |
||
576 | (1 shl 5) + \ ; Async Schedule Enable |
||
577 | (1 shl 4) + \ ; Periodic Schedule Enable |
||
578 | (0 shl 2) + \ ; 1024 elements in FrameList |
||
579 | 1 ; Run |
||
580 | ; 7f. Route all ports to this controller, not companion controllers. |
||
581 | mov dword [edi+EhciConfigFlagReg], 1 |
||
582 | DEBUGF 1,'K : EHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] |
||
583 | ; 8. Apply port power, if needed, and disable all ports. |
||
584 | xor ecx, ecx |
||
585 | @@: |
||
586 | mov dword [edi+EhciPortsReg+ecx*4], 1000h ; Port Power enabled, all other bits disabled |
||
587 | inc ecx |
||
588 | cmp ecx, [esi+usb_controller.NumPorts] |
||
589 | jb @b |
||
590 | test byte [esi+ehci_controller.StructuralParams-sizeof.ehci_controller], 10h |
||
591 | jz @f |
||
592 | push esi |
||
3598 | clevermous | 593 | movi esi, 20 |
4418 | clevermous | 594 | invoke Sleep |
3520 | clevermous | 595 | pop esi |
596 | @@: |
||
597 | ; 9. Return pointer to usb_controller. |
||
598 | xchg eax, esi |
||
599 | ret |
||
600 | ; On error, pop the pointer saved at step 1 and return zero. |
||
601 | ; Note that the main code branch restores the stack at step 7 and never fails |
||
602 | ; after step 7. |
||
603 | .fail_unmap: |
||
604 | pop eax |
||
605 | push eax |
||
4418 | clevermous | 606 | invoke FreeKernelSpace, [eax+ehci_controller.MMIOBase1] |
3520 | clevermous | 607 | .fail: |
608 | pop ecx |
||
609 | xor eax, eax |
||
610 | ret |
||
611 | endp |
||
612 | |||
613 | ; Helper procedure for step 3 of ehci_init, see comments there. |
||
614 | ; Initializes the static head of one list. |
||
615 | ; esi = pointer to the "next" list, edi = pointer to head to initialize. |
||
616 | ; Advances edi to the next head, keeps esi. |
||
617 | proc ehci_init_static_endpoint |
||
618 | xor eax, eax |
||
619 | inc eax ; set Terminate bit |
||
620 | mov [edi+ehci_static_ep.NextTD], eax |
||
621 | mov [edi+ehci_static_ep.AlternateNextTD], eax |
||
622 | test esi, esi |
||
623 | jz @f |
||
624 | mov eax, esi |
||
4418 | clevermous | 625 | invoke GetPhysAddr |
3520 | clevermous | 626 | inc eax |
627 | inc eax ; set Type to EHCI_TYPE_QH |
||
628 | @@: |
||
629 | mov [edi+ehci_static_ep.NextQH], eax |
||
630 | mov [edi+ehci_static_ep.NextList], esi |
||
631 | mov byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted |
||
632 | add edi, ehci_static_ep.SoftwarePart |
||
4418 | clevermous | 633 | mov eax, [USBHCFunc] |
634 | call [eax+usbhc_func.usb_init_static_endpoint] |
||
3520 | clevermous | 635 | add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart |
636 | ret |
||
637 | endp |
||
638 | |||
639 | ; Helper procedure for step 3 of ehci_init, see comments there. |
||
640 | ; Initializes one half of group of static heads. |
||
641 | ; edx = size of the next group = half of size of the group, |
||
642 | ; edi = pointer to the group, esi = pointer to the next group. |
||
643 | ; Advances esi, edi to next group, keeps edx. |
||
644 | proc ehci_init_static_ep_group |
||
645 | push edx |
||
646 | @@: |
||
647 | call ehci_init_static_endpoint |
||
648 | add esi, sizeof.ehci_static_ep |
||
649 | dec edx |
||
650 | jnz @b |
||
651 | pop edx |
||
652 | ret |
||
653 | endp |
||
654 | |||
655 | ; Controller-specific pre-initialization function: take ownership from BIOS. |
||
656 | ; Some BIOSes, although not all of them, use USB controllers themselves |
||
657 | ; to support USB flash drives. In this case, |
||
658 | ; we must notify the BIOS that we don't need that emulation and know how to |
||
659 | ; deal with USB devices. |
||
660 | proc ehci_kickoff_bios |
||
661 | ; 1. Get the physical address of MMIO registers. |
||
4418 | clevermous | 662 | invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 10h |
3520 | clevermous | 663 | and al, not 0Fh |
664 | ; 2. Create mapping for physical memory. 200h bytes are always sufficient. |
||
4418 | clevermous | 665 | invoke MapIoMem, eax, 200h, PG_SW+PG_NOCACHE |
3520 | clevermous | 666 | test eax, eax |
667 | jz .nothing |
||
668 | push eax ; push argument for step 8 |
||
669 | ; 3. Some BIOSes enable controller interrupts as a result of giving |
||
670 | ; controller away. At this point the system knows nothing about how to serve |
||
671 | ; EHCI interrupts, so such an interrupt will send the system into an infinite |
||
672 | ; loop handling the same IRQ again and again. Thus, we need to block EHCI |
||
673 | ; interrupts. We can't do this at the controller level until step 5, |
||
674 | ; because the controller is currently owned by BIOS, so we block all hardware |
||
675 | ; interrupts on this processor until step 5. |
||
676 | pushf |
||
677 | cli |
||
678 | ; 4. Take the ownership over the controller. |
||
679 | ; 4a. Locate take-ownership capability in the PCI configuration space. |
||
680 | ; Limit the loop with 100h iterations; since the entire configuration space is |
||
681 | ; 100h bytes long, hitting this number of iterations means that something is |
||
682 | ; corrupted. |
||
683 | ; Use a value from MMIO as a starting point. |
||
684 | mov edx, [eax+EhciCapParamsReg] |
||
685 | movzx edi, byte [eax+EhciCapLengthReg] |
||
686 | add edi, eax |
||
687 | push 0 |
||
688 | mov bl, dh ; get Extended Capabilities Pointer |
||
689 | test bl, bl |
||
690 | jz .has_ownership2 |
||
691 | cmp bl, 40h |
||
692 | jb .no_capability |
||
693 | .look_bios_handoff: |
||
694 | test bl, 3 |
||
695 | jnz .no_capability |
||
696 | ; In each iteration, read the current dword, |
||
4418 | clevermous | 697 | invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx |
3520 | clevermous | 698 | ; check, whether the capability ID is take-ownership ID = 1, |
699 | cmp al, 1 |
||
700 | jz .found_bios_handoff |
||
701 | ; if not, advance to next-capability link and continue loop. |
||
702 | dec byte [esp] |
||
703 | jz .no_capability |
||
704 | mov bl, ah |
||
705 | cmp bl, 40h |
||
706 | jae .look_bios_handoff |
||
707 | .no_capability: |
||
708 | dbgstr 'warning: cannot locate take-ownership capability' |
||
709 | jmp .has_ownership2 |
||
710 | .found_bios_handoff: |
||
711 | ; 4b. Check whether BIOS has ownership. |
||
712 | ; Some BIOSes release ownership before loading OS, but forget to unwatch for |
||
713 | ; change-ownership requests; they cannot handle ownership request, so |
||
714 | ; such a request sends the system into infinite loop of handling the same SMI |
||
715 | ; over and over. Avoid this. |
||
716 | inc ebx |
||
717 | inc ebx |
||
718 | test eax, 0x10000 |
||
719 | jz .has_ownership |
||
720 | ; 4c. Request ownership. |
||
721 | inc ebx |
||
4418 | clevermous | 722 | invoke PciWrite8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, 1 |
3520 | clevermous | 723 | ; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership |
724 | ; requests; if so, there is no sense in waiting. |
||
725 | inc ebx |
||
4418 | clevermous | 726 | invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx |
3520 | clevermous | 727 | dec ebx |
728 | dec ebx |
||
729 | test ah, 20h |
||
730 | jz .force_ownership |
||
731 | ; 4e. Wait for result no more than 1 s, checking for status every 1 ms. |
||
732 | ; If successful, go to 5. |
||
733 | mov dword [esp], 1000 |
||
734 | @@: |
||
4418 | clevermous | 735 | invoke PciRead8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx |
3520 | clevermous | 736 | test al, 1 |
737 | jz .has_ownership |
||
738 | push esi |
||
3598 | clevermous | 739 | movi esi, 1 |
4418 | clevermous | 740 | invoke Sleep |
3520 | clevermous | 741 | pop esi |
742 | dec dword [esp] |
||
743 | jnz @b |
||
744 | dbgstr 'warning: taking EHCI ownership from BIOS timeout' |
||
745 | .force_ownership: |
||
746 | ; 4f. BIOS has not responded within the timeout. |
||
747 | ; Let's just clear BIOS ownership flag and hope that everything will be ok. |
||
4418 | clevermous | 748 | invoke PciWrite8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, 0 |
3520 | clevermous | 749 | .has_ownership: |
750 | ; 5. Just in case clear all SMI event sources except change-ownership. |
||
751 | dbgstr 'has_ownership' |
||
752 | inc ebx |
||
753 | inc ebx |
||
4418 | clevermous | 754 | invoke PciRead16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx |
3520 | clevermous | 755 | and ax, 2000h |
4418 | clevermous | 756 | invoke PciWrite16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, eax |
3520 | clevermous | 757 | .has_ownership2: |
758 | pop ecx |
||
759 | ; 6. Disable all controller interrupts until the system will be ready to |
||
760 | ; process them. |
||
761 | mov dword [edi+EhciInterruptReg], 0 |
||
762 | ; 7. Now we can unblock interrupts in the processor. |
||
763 | popf |
||
764 | ; 8. Release memory mapping created in step 2 and return. |
||
4418 | clevermous | 765 | invoke FreeKernelSpace |
3520 | clevermous | 766 | .nothing: |
767 | ret |
||
768 | endp |
||
769 | |||
770 | ; IRQ handler for EHCI controllers. |
||
771 | ehci_irq.noint: |
||
772 | spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] |
||
773 | ; Not our interrupt: restore registers and return zero. |
||
774 | xor eax, eax |
||
775 | pop edi esi ebx |
||
776 | ret |
||
777 | |||
778 | proc ehci_irq |
||
779 | push ebx esi edi ; save registers to be cdecl |
||
780 | virtual at esp |
||
781 | rd 3 ; saved registers |
||
782 | dd ? ; return address |
||
783 | .controller dd ? |
||
784 | end virtual |
||
785 | ; 1. ebx will hold whether some deferred processing is needed, |
||
786 | ; that cannot be done from the interrupt handler. Initialize to zero. |
||
787 | xor ebx, ebx |
||
788 | ; 2. Get the mask of events which should be processed. |
||
789 | mov esi, [.controller] |
||
790 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
||
791 | spin_lock_irqsave [esi+usb_controller.WaitSpinlock] |
||
792 | mov eax, [edi+EhciStatusReg] |
||
793 | ; 3. Check whether that interrupt has been generated by our controller. |
||
794 | ; (One IRQ can be shared by several devices.) |
||
795 | and eax, [edi+EhciInterruptReg] |
||
796 | jz .noint |
||
797 | ; 4. Clear the events we know of. |
||
798 | ; Note that this should be done before processing of events: |
||
799 | ; new events could arise while we are processing those, this way we won't lose |
||
800 | ; them (the controller would generate another interrupt after completion |
||
801 | ; of this one). |
||
802 | ; DEBUGF 1,'K : EHCI interrupt: status = %x\n',eax |
||
803 | mov [edi+EhciStatusReg], eax |
||
804 | ; 5. Sanity check. |
||
805 | test al, 10h |
||
806 | jz @f |
||
807 | DEBUGF 1,'K : something terrible happened with EHCI %x (%x)\n',esi,al |
||
808 | @@: |
||
809 | ; We can't do too much from an interrupt handler. Inform the processing thread |
||
810 | ; that it should perform appropriate actions. |
||
811 | or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax |
||
812 | spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] |
||
813 | inc ebx |
||
4418 | clevermous | 814 | invoke usbhc_api.usb_wakeup_if_needed |
3520 | clevermous | 815 | ; 6. Interrupt processed; return non-zero. |
816 | mov al, 1 |
||
817 | pop edi esi ebx ; restore used registers to be cdecl |
||
818 | ret |
||
819 | endp |
||
820 | |||
821 | ; This procedure is called from usb_set_address_callback |
||
822 | ; and stores USB device address in the ehci_pipe structure. |
||
823 | ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address |
||
824 | proc ehci_set_device_address |
||
3653 | clevermous | 825 | mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl |
4418 | clevermous | 826 | jmp [usbhc_api.usb_subscribe_control] |
3520 | clevermous | 827 | endp |
828 | |||
829 | ; This procedure returns USB device address from the ehci_pipe structure. |
||
830 | ; in: esi -> usb_controller, ebx -> usb_pipe |
||
831 | ; out: eax = endpoint address |
||
832 | proc ehci_get_device_address |
||
3653 | clevermous | 833 | mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
3520 | clevermous | 834 | and eax, 7Fh |
835 | ret |
||
836 | endp |
||
837 | |||
838 | ; This procedure is called from usb_set_address_callback |
||
839 | ; if the device does not accept SET_ADDRESS command and needs |
||
840 | ; to be disabled at the port level. |
||
841 | ; in: esi -> usb_controller, ecx = port (zero-based) |
||
842 | proc ehci_port_disable |
||
843 | mov eax, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
||
844 | and dword [eax+EhciPortsReg+ecx*4], not (4 or 2Ah) |
||
845 | ret |
||
846 | endp |
||
847 | |||
848 | ; This procedure is called from usb_get_descr8_callback when |
||
849 | ; the packet size for zero endpoint becomes known and |
||
850 | ; stores the packet size in ehci_pipe structure. |
||
851 | ; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size |
||
852 | proc ehci_set_endpoint_packet_size |
||
3653 | clevermous | 853 | mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
3520 | clevermous | 854 | and eax, not (0x7FF shl 16) |
855 | shl ecx, 16 |
||
856 | or eax, ecx |
||
3653 | clevermous | 857 | mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax |
3520 | clevermous | 858 | ; Wait until hardware cache is evicted. |
4418 | clevermous | 859 | jmp [usbhc_api.usb_subscribe_control] |
3520 | clevermous | 860 | endp |
861 | |||
862 | uglobal |
||
863 | align 4 |
||
864 | ; Data for memory allocator, see memory.inc. |
||
865 | ehci_ep_first_page dd ? |
||
866 | ehci_ep_mutex MUTEX |
||
867 | ehci_gtd_first_page dd ? |
||
868 | ehci_gtd_mutex MUTEX |
||
869 | endg |
||
870 | |||
871 | ; This procedure allocates memory for pipe. |
||
872 | ; Both hardware+software parts must be allocated, returns pointer to usb_pipe |
||
873 | ; (software part). |
||
874 | proc ehci_alloc_pipe |
||
875 | push ebx |
||
876 | mov ebx, ehci_ep_mutex |
||
4418 | clevermous | 877 | invoke usbhc_api.usb_allocate_common, (sizeof.ehci_pipe + sizeof.usb_pipe + 1Fh) and not 1Fh |
3520 | clevermous | 878 | test eax, eax |
879 | jz @f |
||
3653 | clevermous | 880 | add eax, sizeof.ehci_pipe |
3520 | clevermous | 881 | @@: |
882 | pop ebx |
||
883 | ret |
||
884 | endp |
||
885 | |||
886 | ; This procedure frees memory for pipe allocated by ehci_alloc_pipe. |
||
887 | ; void stdcall with one argument = pointer to usb_pipe. |
||
888 | proc ehci_free_pipe |
||
889 | virtual at esp |
||
890 | dd ? ; return address |
||
891 | .ptr dd ? |
||
892 | end virtual |
||
3653 | clevermous | 893 | sub [.ptr], sizeof.ehci_pipe |
4418 | clevermous | 894 | jmp [usbhc_api.usb_free_common] |
3520 | clevermous | 895 | endp |
896 | |||
897 | ; This procedure is called from API usb_open_pipe and processes |
||
898 | ; the controller-specific part of this API. See docs. |
||
899 | ; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe, |
||
900 | ; esi -> usb_controller, eax -> usb_gtd for the first TD, |
||
901 | ; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type |
||
902 | proc ehci_init_pipe |
||
903 | virtual at ebp+8 |
||
904 | .config_pipe dd ? |
||
905 | .endpoint dd ? |
||
906 | .maxpacket dd ? |
||
907 | .type dd ? |
||
908 | .interval dd ? |
||
909 | end virtual |
||
910 | ; 1. Zero all fields in the hardware part. |
||
911 | push eax ecx |
||
3653 | clevermous | 912 | sub edi, sizeof.ehci_pipe |
3520 | clevermous | 913 | xor eax, eax |
3653 | clevermous | 914 | movi ecx, sizeof.ehci_pipe/4 |
3520 | clevermous | 915 | rep stosd |
916 | pop ecx eax |
||
917 | ; 2. Setup PID in the first TD and make sure that the it is not active. |
||
918 | xor edx, edx |
||
919 | test byte [.endpoint], 80h |
||
920 | setnz dh |
||
3653 | clevermous | 921 | mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
922 | mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
||
923 | mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
||
3520 | clevermous | 924 | ; 3. Store physical address of the first TD. |
3653 | clevermous | 925 | sub eax, sizeof.ehci_gtd |
4418 | clevermous | 926 | call [GetPhysAddr] |
3653 | clevermous | 927 | mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
3520 | clevermous | 928 | ; 4. Fill ehci_pipe.Flags except for S- and C-masks. |
929 | ; Copy location from the config pipe. |
||
3653 | clevermous | 930 | mov eax, [ecx+ehci_pipe.Flags-sizeof.ehci_pipe] |
3520 | clevermous | 931 | and eax, 3FFF0000h |
932 | ; Use 1 requests per microframe for control/bulk endpoints, |
||
933 | ; use value from the endpoint descriptor for periodic endpoints |
||
3598 | clevermous | 934 | movi edx, 1 |
3520 | clevermous | 935 | test [.type], 1 |
936 | jz @f |
||
937 | mov edx, [.maxpacket] |
||
938 | shr edx, 11 |
||
939 | inc edx |
||
940 | @@: |
||
941 | shl edx, 30 |
||
942 | or eax, edx |
||
3653 | clevermous | 943 | mov [edi+ehci_pipe.Flags-sizeof.ehci_pipe], eax |
3520 | clevermous | 944 | ; 5. Fill ehci_pipe.Token. |
3653 | clevermous | 945 | mov eax, [ecx+ehci_pipe.Token-sizeof.ehci_pipe] |
3520 | clevermous | 946 | ; copy following fields from the config pipe: |
947 | ; DeviceAddress, EndpointSpeed, ControlEndpoint if new type is control |
||
948 | mov ecx, eax |
||
949 | and eax, 307Fh |
||
950 | and ecx, 8000000h |
||
951 | or ecx, 4000h |
||
952 | mov edx, [.endpoint] |
||
953 | and edx, 15 |
||
954 | shl edx, 8 |
||
955 | or eax, edx |
||
956 | mov edx, [.maxpacket] |
||
957 | shl edx, 16 |
||
958 | or eax, edx |
||
959 | ; for control endpoints, use DataToggle from TD, otherwise use DataToggle from QH |
||
960 | cmp [.type], CONTROL_PIPE |
||
961 | jnz @f |
||
962 | or eax, ecx |
||
963 | @@: |
||
964 | ; for control/bulk USB2 endpoints, set NakCountReload to 4 |
||
965 | test eax, USB_SPEED_HS shl 12 |
||
966 | jz .nonak |
||
967 | cmp [.type], CONTROL_PIPE |
||
968 | jz @f |
||
969 | cmp [.type], BULK_PIPE |
||
970 | jnz .nonak |
||
971 | @@: |
||
972 | or eax, 40000000h |
||
973 | .nonak: |
||
3653 | clevermous | 974 | mov [edi+ehci_pipe.Token-sizeof.ehci_pipe], eax |
3520 | clevermous | 975 | ; 5. Select the corresponding list and insert to the list. |
976 | ; 5a. Use Control list for control pipes, Bulk list for bulk pipes. |
||
977 | lea edx, [esi+ehci_controller.ControlED.SoftwarePart-sizeof.ehci_controller] |
||
978 | cmp [.type], BULK_PIPE |
||
979 | jb .insert ; control pipe |
||
980 | lea edx, [esi+ehci_controller.BulkED.SoftwarePart-sizeof.ehci_controller] |
||
981 | jz .insert ; bulk pipe |
||
982 | .interrupt_pipe: |
||
983 | ; 5b. For interrupt pipes, let the scheduler select the appropriate list |
||
984 | ; and the appropriate microframe(s) (which goes to S-mask and C-mask) |
||
985 | ; based on the current bandwidth distribution and the requested bandwidth. |
||
986 | ; There are two schedulers, one for high-speed devices, |
||
987 | ; another for split transactions. |
||
988 | ; This could fail if the requested bandwidth is not available; |
||
989 | ; if so, return an error. |
||
3653 | clevermous | 990 | test word [edi+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
3826 | clevermous | 991 | jnz .interrupt_tt |
3520 | clevermous | 992 | call ehci_select_hs_interrupt_list |
993 | jmp .interrupt_common |
||
3826 | clevermous | 994 | .interrupt_tt: |
995 | call ehci_select_tt_interrupt_list |
||
3520 | clevermous | 996 | .interrupt_common: |
997 | test edx, edx |
||
998 | jz .return0 |
||
3653 | clevermous | 999 | mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax |
3520 | clevermous | 1000 | .insert: |
3653 | clevermous | 1001 | mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx |
3520 | clevermous | 1002 | ; Insert to the head of the corresponding list. |
1003 | ; Note: inserting to the head guarantees that the list traverse in |
||
1004 | ; ehci_process_updated_schedule, once started, will not interact with new pipes. |
||
1005 | ; However, we still need to ensure that links in the new pipe (edi.NextVirt) |
||
1006 | ; are initialized before links to the new pipe (edx.NextVirt). |
||
1007 | ; 5c. Insert in the list of virtual addresses. |
||
1008 | mov ecx, [edx+usb_pipe.NextVirt] |
||
1009 | mov [edi+usb_pipe.NextVirt], ecx |
||
1010 | mov [edi+usb_pipe.PrevVirt], edx |
||
1011 | mov [ecx+usb_pipe.PrevVirt], edi |
||
1012 | mov [edx+usb_pipe.NextVirt], edi |
||
1013 | ; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, |
||
1014 | ; store the physical address of the new pipe to previous NextQH. |
||
1015 | mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] |
||
3653 | clevermous | 1016 | mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx |
1017 | lea eax, [edi-sizeof.ehci_pipe] |
||
4418 | clevermous | 1018 | call [GetPhysAddr] |
3520 | clevermous | 1019 | inc eax |
1020 | inc eax |
||
1021 | mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax |
||
1022 | ; 6. Return with nonzero eax. |
||
1023 | ret |
||
1024 | .return0: |
||
1025 | xor eax, eax |
||
1026 | ret |
||
1027 | endp |
||
1028 | |||
1029 | ; This function is called from ehci_process_deferred when |
||
1030 | ; a new device was connected at least USB_CONNECT_DELAY ticks |
||
1031 | ; and therefore is ready to be configured. |
||
1032 | ; ecx = port, esi -> ehci_controller, edi -> EHCI MMIO |
||
1033 | proc ehci_new_port |
||
1034 | ; 1. If the device operates at low-speed, just release it to a companion. |
||
1035 | mov eax, [edi+EhciPortsReg+ecx*4] |
||
4418 | clevermous | 1036 | DEBUGF 1,'K : EHCI %x port %d state is %x\n',esi,ecx,eax |
3520 | clevermous | 1037 | mov edx, eax |
1038 | and ah, 0Ch |
||
1039 | cmp ah, 4 |
||
1040 | jz .low_speed |
||
1041 | ; 2. Devices operating at full-speed and high-speed must now have ah == 8. |
||
1042 | ; Some broken hardware asserts both D+ and D- even after initial decoupling; |
||
1043 | ; if so, stop initialization here, no sense in further actions. |
||
1044 | cmp ah, 0Ch |
||
1045 | jz .se1 |
||
1046 | ; 3. If another port is resetting right now, mark this port as 'reset pending' |
||
1047 | ; and return. |
||
1048 | bts [esi+usb_controller.PendingPorts], ecx |
||
1049 | cmp [esi+usb_controller.ResettingPort], -1 |
||
1050 | jnz .nothing |
||
1051 | btr [esi+usb_controller.PendingPorts], ecx |
||
1052 | ; Otherwise, fall through to ohci_new_port.reset. |
||
1053 | |||
1054 | ; This function is called from ehci_new_port and usb_test_pending_port. |
||
1055 | ; It starts reset signalling for the port. Note that in USB first stages |
||
1056 | ; of configuration can not be done for several ports in parallel. |
||
1057 | .reset: |
||
1058 | push edi |
||
1059 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
||
1060 | mov eax, [edi+EhciPortsReg+ecx*4] |
||
1061 | ; 1. Store information about resetting hub (roothub) and port. |
||
1062 | and [esi+usb_controller.ResettingHub], 0 |
||
1063 | mov [esi+usb_controller.ResettingPort], cl |
||
1064 | ; 2. Initiate reset signalling. |
||
1065 | or ah, 1 |
||
1066 | and al, not (4 or 2Ah) |
||
1067 | mov [edi+EhciPortsReg+ecx*4], eax |
||
1068 | ; 3. Store the current time and set status to 1 = reset signalling active. |
||
4418 | clevermous | 1069 | invoke GetTimerTicks |
3520 | clevermous | 1070 | mov [esi+usb_controller.ResetTime], eax |
1071 | mov [esi+usb_controller.ResettingStatus], 1 |
||
1072 | ; dbgstr 'high-speed or full-speed device, resetting' |
||
4418 | clevermous | 1073 | DEBUGF 1,'K : EHCI %x: port %d has HS or FS device, resetting\n',esi,ecx |
3520 | clevermous | 1074 | pop edi |
1075 | .nothing: |
||
1076 | ret |
||
1077 | .low_speed: |
||
1078 | ; dbgstr 'low-speed device, releasing' |
||
4418 | clevermous | 1079 | DEBUGF 1,'K : EHCI %x: port %d has LS device, releasing\n',esi,ecx |
3520 | clevermous | 1080 | or dh, 20h |
1081 | and dl, not 2Ah |
||
1082 | mov [edi+EhciPortsReg+ecx*4], edx |
||
1083 | ret |
||
1084 | .se1: |
||
1085 | dbgstr 'SE1 after connect debounce. Broken hardware?' |
||
1086 | ret |
||
1087 | endp |
||
1088 | |||
1089 | ; This procedure is called from several places in main USB code |
||
1090 | ; and allocates required packets for the given transfer. |
||
1091 | ; ebx = pipe, other parameters are passed through the stack: |
||
1092 | ; buffer,size = data to transfer |
||
1093 | ; flags = same as in usb_open_pipe: bit 0 = allow short transfer, other bits reserved |
||
1094 | ; td = pointer to the current end-of-queue descriptor |
||
1095 | ; direction = |
||
1096 | ; 0000b for normal transfers, |
||
1097 | ; 1000b for control SETUP transfer, |
||
1098 | ; 1101b for control OUT transfer, |
||
1099 | ; 1110b for control IN transfer |
||
1100 | ; returns eax = pointer to the new end-of-queue descriptor |
||
1101 | ; (not included in the queue itself) or 0 on error |
||
1102 | proc ehci_alloc_transfer stdcall uses edi, \ |
||
1103 | buffer:dword, size:dword, flags:dword, td:dword, direction:dword |
||
1104 | locals |
||
1105 | origTD dd ? |
||
1106 | packetSize dd ? ; must be last variable, see usb_init_transfer |
||
1107 | endl |
||
1108 | ; 1. Save original value of td: |
||
1109 | ; it will be useful for rollback if something would fail. |
||
1110 | mov eax, [td] |
||
1111 | mov [origTD], eax |
||
1112 | ; One transfer descriptor can describe up to 5 pages. |
||
1113 | ; In the worst case (when the buffer is something*1000h+0FFFh) |
||
1114 | ; this corresponds to 4001h bytes. If the requested size is |
||
1115 | ; greater, we should split the transfer into several descriptors. |
||
1116 | ; Boundaries to split must be multiples of endpoint transfer size |
||
3656 | clevermous | 1117 | ; to avoid short packets except in the end of the transfer. |
1118 | cmp [size], 4001h |
||
1119 | jbe .lastpacket |
||
3520 | clevermous | 1120 | ; 2. While the remaining data cannot fit in one descriptor, |
1121 | ; allocate full descriptors (of maximal possible size). |
||
3656 | clevermous | 1122 | ; 2a. Calculate size of one descriptor: must be a multiple of transfer size |
1123 | ; and must be not greater than 4001h. |
||
4418 | clevermous | 1124 | movzx ecx, word [ebx+ehci_pipe.Token+2-sizeof.ehci_pipe] |
1125 | and ecx, (1 shl 11) - 1 |
||
3656 | clevermous | 1126 | mov eax, 4001h |
1127 | xor edx, edx |
||
1128 | mov edi, eax |
||
1129 | div ecx |
||
1130 | sub edi, edx |
||
3520 | clevermous | 1131 | mov [packetSize], edi |
1132 | .fullpackets: |
||
1133 | call ehci_alloc_packet |
||
1134 | test eax, eax |
||
1135 | jz .fail |
||
1136 | mov [td], eax |
||
1137 | add [buffer], edi |
||
1138 | sub [size], edi |
||
3656 | clevermous | 1139 | cmp [size], 4001h |
1140 | ja .fullpackets |
||
3520 | clevermous | 1141 | ; 3. The remaining data can fit in one packet; |
1142 | ; allocate the last descriptor with size = size of remaining data. |
||
1143 | .lastpacket: |
||
1144 | mov eax, [size] |
||
1145 | mov [packetSize], eax |
||
1146 | call ehci_alloc_packet |
||
1147 | test eax, eax |
||
1148 | jz .fail |
||
1149 | ; 9. Update flags in the last packet. |
||
1150 | mov edx, [flags] |
||
3653 | clevermous | 1151 | mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], edx |
3520 | clevermous | 1152 | ; 10. Fill AlternateNextTD field in all allocated TDs. |
1153 | ; If the caller says that short transfer is ok, the queue must advance to |
||
1154 | ; the next descriptor, which is in eax. |
||
1155 | ; Otherwise, the queue should stop, so make AlternateNextTD point to |
||
1156 | ; always-inactive descriptor StopQueueTD. |
||
1157 | push eax |
||
1158 | test dl, 1 |
||
1159 | jz .disable_short |
||
3653 | clevermous | 1160 | sub eax, sizeof.ehci_gtd |
3520 | clevermous | 1161 | jmp @f |
1162 | .disable_short: |
||
1163 | mov eax, [ebx+usb_pipe.Controller] |
||
1164 | add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller |
||
1165 | @@: |
||
4418 | clevermous | 1166 | call [GetPhysAddr] |
3520 | clevermous | 1167 | mov edx, [origTD] |
1168 | @@: |
||
1169 | cmp edx, [esp] |
||
1170 | jz @f |
||
3653 | clevermous | 1171 | mov [edx+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], eax |
3520 | clevermous | 1172 | mov edx, [edx+usb_gtd.NextVirt] |
1173 | jmp @b |
||
1174 | @@: |
||
1175 | pop eax |
||
1176 | ret |
||
1177 | .fail: |
||
1178 | mov edi, ehci_hardware_func |
||
1179 | mov eax, [td] |
||
4418 | clevermous | 1180 | invoke usbhc_api.usb_undo_tds, [origTD] |
3520 | clevermous | 1181 | xor eax, eax |
1182 | ret |
||
1183 | endp |
||
1184 | |||
1185 | ; Helper procedure for ehci_alloc_transfer. |
||
1186 | ; Allocates and initializes one transfer descriptor. |
||
1187 | ; ebx = pipe, other parameters are passed through the stack; |
||
1188 | ; fills the current last descriptor and |
||
1189 | ; returns eax = next descriptor (not filled). |
||
1190 | proc ehci_alloc_packet |
||
1191 | ; inherit some variables from the parent ehci_alloc_transfer |
||
1192 | virtual at ebp-8 |
||
1193 | .origTD dd ? |
||
1194 | .packetSize dd ? |
||
1195 | rd 2 |
||
1196 | .buffer dd ? |
||
1197 | .transferSize dd ? |
||
1198 | .Flags dd ? |
||
1199 | .td dd ? |
||
1200 | .direction dd ? |
||
1201 | end virtual |
||
1202 | ; 1. Allocate the next TD. |
||
1203 | call ehci_alloc_td |
||
1204 | test eax, eax |
||
1205 | jz .nothing |
||
1206 | ; 2. Initialize controller-independent parts of both TDs. |
||
1207 | push eax |
||
4418 | clevermous | 1208 | invoke usbhc_api.usb_init_transfer |
3520 | clevermous | 1209 | pop eax |
1210 | ; 3. Copy PID to the new descriptor. |
||
3653 | clevermous | 1211 | mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
1212 | mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
||
1213 | mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
||
1214 | mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
||
3520 | clevermous | 1215 | ; 4. Save the returned value (next descriptor). |
1216 | push eax |
||
1217 | ; 5. Store the physical address of the next descriptor. |
||
3653 | clevermous | 1218 | sub eax, sizeof.ehci_gtd |
4418 | clevermous | 1219 | call [GetPhysAddr] |
3653 | clevermous | 1220 | mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax |
3520 | clevermous | 1221 | ; 6. For zero-length transfers, store zero in all fields for buffer addresses. |
1222 | ; Otherwise, fill them with real values. |
||
1223 | xor eax, eax |
||
3653 | clevermous | 1224 | mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], eax |
3520 | clevermous | 1225 | repeat 10 |
3653 | clevermous | 1226 | mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd+(%-1)*4], eax |
3520 | clevermous | 1227 | end repeat |
1228 | cmp [.packetSize], eax |
||
1229 | jz @f |
||
1230 | mov eax, [.buffer] |
||
4418 | clevermous | 1231 | call [GetPhysAddr] |
3653 | clevermous | 1232 | mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax |
3520 | clevermous | 1233 | and eax, 0xFFF |
1234 | mov edx, [.packetSize] |
||
1235 | add edx, eax |
||
1236 | sub edx, 0x1000 |
||
1237 | jbe @f |
||
1238 | mov eax, [.buffer] |
||
1239 | add eax, 0x1000 |
||
4418 | clevermous | 1240 | call [GetPgAddr] |
3653 | clevermous | 1241 | mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax |
3520 | clevermous | 1242 | sub edx, 0x1000 |
1243 | jbe @f |
||
1244 | mov eax, [.buffer] |
||
1245 | add eax, 0x2000 |
||
4418 | clevermous | 1246 | call [GetPgAddr] |
3653 | clevermous | 1247 | mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax |
3520 | clevermous | 1248 | sub edx, 0x1000 |
1249 | jbe @f |
||
1250 | mov eax, [.buffer] |
||
1251 | add eax, 0x3000 |
||
4418 | clevermous | 1252 | call [GetPgAddr] |
3653 | clevermous | 1253 | mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax |
3520 | clevermous | 1254 | sub edx, 0x1000 |
1255 | jbe @f |
||
1256 | mov eax, [.buffer] |
||
1257 | add eax, 0x4000 |
||
4418 | clevermous | 1258 | call [GetPgAddr] |
3653 | clevermous | 1259 | mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax |
3520 | clevermous | 1260 | @@: |
1261 | ; 7. Fill Token field: |
||
1262 | ; set Status = 0 (inactive, ehci_insert_transfer would mark everything active); |
||
1263 | ; keep current PID if [.direction] is zero, use two lower bits of [.direction] |
||
1264 | ; otherwise shifted as (0|1|2) -> (2|0|1); |
||
1265 | ; set error counter to 3; |
||
1266 | ; set current page to 0; |
||
1267 | ; do not interrupt on complete (ehci_insert_transfer sets this bit where needed); |
||
1268 | ; set DataToggle to bit 2 of [.direction]. |
||
3653 | clevermous | 1269 | mov eax, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
3520 | clevermous | 1270 | and eax, 300h ; keep PID code |
1271 | mov edx, [.direction] |
||
1272 | test edx, edx |
||
1273 | jz .haspid |
||
1274 | and edx, 3 |
||
1275 | dec edx |
||
1276 | jns @f |
||
1277 | add edx, 3 |
||
1278 | @@: |
||
1279 | mov ah, dl |
||
1280 | mov edx, [.direction] |
||
1281 | and edx, not 3 |
||
1282 | shl edx, 29 |
||
1283 | or eax, edx |
||
1284 | .haspid: |
||
1285 | or eax, 0C00h |
||
1286 | mov edx, [.packetSize] |
||
1287 | shl edx, 16 |
||
1288 | or eax, edx |
||
3653 | clevermous | 1289 | mov [ecx+ehci_gtd.Token-sizeof.ehci_gtd], eax |
3520 | clevermous | 1290 | ; 4. Restore the returned value saved in step 2. |
1291 | pop eax |
||
1292 | .nothing: |
||
1293 | ret |
||
1294 | endp |
||
1295 | |||
1296 | ; This procedure is called from several places in main USB code |
||
1297 | ; and activates the transfer which was previously allocated by |
||
1298 | ; ehci_alloc_transfer. |
||
1299 | ; ecx -> last descriptor for the transfer, ebx -> usb_pipe |
||
1300 | proc ehci_insert_transfer |
||
3653 | clevermous | 1301 | or byte [ecx+ehci_gtd.Token+1-sizeof.ehci_gtd], 80h ; set IOC bit |
3520 | clevermous | 1302 | mov eax, [esp+4] |
1303 | .activate: |
||
3653 | clevermous | 1304 | or byte [eax+ehci_gtd.Token-sizeof.ehci_gtd], 80h ; set Active bit |
3520 | clevermous | 1305 | cmp eax, ecx |
1306 | mov eax, [eax+usb_gtd.NextVirt] |
||
1307 | jnz .activate |
||
1308 | ret |
||
1309 | endp |
||
1310 | |||
1311 | ; This function is called from ehci_process_deferred when |
||
1312 | ; reset signalling for a new device needs to be finished. |
||
1313 | proc ehci_port_reset_done |
||
1314 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
1315 | and dword [edi+EhciPortsReg+ecx*4], not 12Ah |
||
4418 | clevermous | 1316 | invoke GetTimerTicks |
3520 | clevermous | 1317 | mov [esi+usb_controller.ResetTime], eax |
1318 | mov [esi+usb_controller.ResettingStatus], 2 |
||
4418 | clevermous | 1319 | ; DEBUGF 1,'K : EHCI %x: reset port %d done\n',esi,ecx |
3520 | clevermous | 1320 | ret |
1321 | endp |
||
1322 | |||
1323 | ; This function is called from ehci_process_deferred when |
||
1324 | ; a new device has been reset, recovered after reset and needs to be configured. |
||
1325 | proc ehci_port_init |
||
1326 | ; 1. Get the status and set it to zero. |
||
1327 | ; If reset has been failed (device disconnected during reset), |
||
1328 | ; continue to next device (if there is one). |
||
1329 | xor eax, eax |
||
1330 | xchg al, [esi+usb_controller.ResettingStatus] |
||
1331 | test al, al |
||
4418 | clevermous | 1332 | jns @f |
1333 | jmp [usbhc_api.usb_test_pending_port] |
||
1334 | @@: |
||
3520 | clevermous | 1335 | ; 2. Get the port status. High-speed devices should be now enabled, |
1336 | ; full-speed devices are left disabled; |
||
1337 | ; if the port is disabled, release it to a companion and continue to |
||
1338 | ; next device (if there is one). |
||
1339 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
1340 | mov eax, [edi+EhciPortsReg+ecx*4] |
||
4418 | clevermous | 1341 | DEBUGF 1,'K : EHCI %x status of port %d is %x\n',esi,ecx,eax |
3520 | clevermous | 1342 | test al, 4 |
1343 | jnz @f |
||
1344 | ; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax |
||
1345 | dbgstr 'releasing to companion' |
||
1346 | or ah, 20h |
||
1347 | mov [edi+EhciPortsReg+ecx*4], eax |
||
4418 | clevermous | 1348 | jmp [usbhc_api.usb_test_pending_port] |
3520 | clevermous | 1349 | @@: |
1350 | ; 3. Call the worker procedure to notify the protocol layer |
||
1351 | ; about new EHCI device. It is high-speed. |
||
3598 | clevermous | 1352 | movi eax, USB_SPEED_HS |
3520 | clevermous | 1353 | call ehci_new_device |
1354 | test eax, eax |
||
1355 | jnz .nothing |
||
1356 | ; 4. If something at the protocol layer has failed |
||
1357 | ; (no memory, no bus address), disable the port and stop the initialization. |
||
1358 | .disable_exit: |
||
1359 | and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah) |
||
4418 | clevermous | 1360 | jmp [usbhc_api.usb_test_pending_port] |
3520 | clevermous | 1361 | .nothing: |
1362 | ret |
||
1363 | endp |
||
1364 | |||
1365 | ; This procedure is called from ehci_port_init and from hub support code |
||
1366 | ; when a new device is connected and has been reset. |
||
1367 | ; It calls usb_new_device at the protocol layer with correct parameters. |
||
1368 | ; in: esi -> usb_controller, eax = speed. |
||
1369 | proc ehci_new_device |
||
1370 | push ebx ecx ; save used registers (ecx is important for ehci_port_init) |
||
1371 | ; 1. Store the speed for the protocol layer. |
||
1372 | mov [esi+usb_controller.ResettingSpeed], al |
||
1373 | ; 2. Shift speed bits to the proper place in ehci_pipe.Token. |
||
1374 | shl eax, 12 |
||
1375 | ; 3. For high-speed devices, go to step 5 with edx = 0. |
||
1376 | xor edx, edx |
||
1377 | cmp ah, USB_SPEED_HS shl (12-8) |
||
1378 | jz .common |
||
1379 | ; 4. For low-speed and full-speed devices, fill address:port |
||
1380 | ; of the last high-speed hub (the closest to the device hub) |
||
1381 | ; for split transactions, and set ControlEndpoint bit in eax; |
||
1382 | ; ehci_init_pipe assumes that the parent pipe is a control pipe. |
||
4418 | clevermous | 1383 | push eax |
3520 | clevermous | 1384 | movzx ecx, [esi+usb_controller.ResettingPort] |
1385 | mov edx, [esi+usb_controller.ResettingHub] |
||
4418 | clevermous | 1386 | invoke usbhc_api.usb_get_tt |
3520 | clevermous | 1387 | inc ecx |
3653 | clevermous | 1388 | mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] |
3520 | clevermous | 1389 | shl ecx, 23 |
1390 | and edx, 7Fh |
||
1391 | shl edx, 16 |
||
1392 | or edx, ecx ; ehci_pipe.Flags |
||
1393 | pop eax |
||
1394 | or eax, 1 shl 27 ; ehci_pipe.Token |
||
1395 | .common: |
||
1396 | ; 5. Create pseudo-pipe in the stack. |
||
1397 | ; See ehci_init_pipe: only .Controller, .Token, .Flags fields are used. |
||
3653 | clevermous | 1398 | push esi ; usb_pipe.Controller |
3520 | clevermous | 1399 | mov ecx, esp |
3653 | clevermous | 1400 | sub esp, sizeof.ehci_pipe - ehci_pipe.Flags - 4 |
3520 | clevermous | 1401 | push edx ; ehci_pipe.Flags |
1402 | push eax ; ehci_pipe.Token |
||
1403 | ; 6. Notify the protocol layer. |
||
4418 | clevermous | 1404 | invoke usbhc_api.usb_new_device |
3520 | clevermous | 1405 | ; 7. Cleanup the stack after step 5 and return. |
3653 | clevermous | 1406 | add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 |
3520 | clevermous | 1407 | pop ecx ebx ; restore used registers |
1408 | ret |
||
1409 | endp |
||
1410 | |||
1411 | ; This procedure is called in the USB thread from usb_thread_proc, |
||
1412 | ; processes regular actions and those actions which can't be safely done |
||
1413 | ; from interrupt handler. |
||
1414 | ; Returns maximal time delta before the next call. |
||
1415 | proc ehci_process_deferred |
||
1416 | push ebx edi ; save used registers to be stdcall |
||
1417 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
||
1418 | ; 1. Get the mask of events to process. |
||
1419 | xor eax, eax |
||
1420 | xchg eax, [esi+ehci_controller.DeferredActions-sizeof.ehci_controller] |
||
1421 | push eax |
||
1422 | ; 2. Initialize the return value. |
||
1423 | push -1 |
||
1424 | ; Handle roothub events. |
||
1425 | ; 3a. Test whether there are such events. |
||
1426 | test al, 4 |
||
1427 | jz .skip_roothub |
||
1428 | ; Status of some port has changed. Loop over all ports. |
||
1429 | ; 3b. Prepare for the loop: start from port 0. |
||
1430 | xor ecx, ecx |
||
1431 | .portloop: |
||
1432 | ; 3c. Get the port status and changes of it. |
||
1433 | ; If there are no changes, just continue to the next port. |
||
1434 | mov eax, [edi+EhciPortsReg+ecx*4] |
||
1435 | test al, 2Ah |
||
1436 | jz .nextport |
||
1437 | ; 3d. Clear change bits and read the status again. |
||
1438 | ; (It is possible, although quite unlikely, that some event occurs between |
||
1439 | ; the first read and the clearing, invalidating the old status. If an event |
||
1440 | ; occurs after the clearing, we will not miss it, looking in the next scan. |
||
1441 | mov [edi+EhciPortsReg+ecx*4], eax |
||
1442 | mov ebx, eax |
||
1443 | mov eax, [edi+EhciPortsReg+ecx*4] |
||
4418 | clevermous | 1444 | DEBUGF 1,'K : EHCI %x: status of port %d changed to %x\n',esi,ecx,ebx |
3520 | clevermous | 1445 | ; 3e. Handle overcurrent. |
1446 | ; Note: that needs work. |
||
1447 | test bl, 20h ; overcurrent change |
||
1448 | jz .noovercurrent |
||
1449 | test al, 10h ; overcurrent active |
||
1450 | jz .noovercurrent |
||
1451 | DEBUGF 1,'K : overcurrent at port %d\n',ecx |
||
1452 | .noovercurrent: |
||
1453 | ; 3f. Handle changing of connection status. |
||
1454 | test bl, 2 |
||
1455 | jz .nocsc |
||
1456 | ; There was a connect or disconnect event at this port. |
||
1457 | ; 3g. Disconnect the old device on this port, if any. |
||
1458 | ; If the port was resetting, indicate fail; later stages will process it. |
||
4300 | clevermous | 1459 | ; Ignore connect event immediately after resetting. |
3520 | clevermous | 1460 | cmp [esi+usb_controller.ResettingHub], 0 |
4300 | clevermous | 1461 | jnz .csc.noreset |
3520 | clevermous | 1462 | cmp cl, [esi+usb_controller.ResettingPort] |
4300 | clevermous | 1463 | jnz .csc.noreset |
1464 | cmp [esi+usb_controller.ResettingStatus], 2 |
||
3520 | clevermous | 1465 | jnz @f |
4300 | clevermous | 1466 | test al, 1 |
1467 | jnz .nextport |
||
1468 | @@: |
||
3520 | clevermous | 1469 | mov [esi+usb_controller.ResettingStatus], -1 |
4300 | clevermous | 1470 | .csc.noreset: |
3520 | clevermous | 1471 | bts [esi+usb_controller.NewDisconnected], ecx |
1472 | ; 3h. Change connected status. For the connection event, also store |
||
1473 | ; the connection time; any further processing is permitted only after |
||
1474 | ; USB_CONNECT_DELAY ticks. |
||
1475 | test al, 1 |
||
1476 | jz .disconnect |
||
4418 | clevermous | 1477 | invoke GetTimerTicks |
3520 | clevermous | 1478 | mov [esi+usb_controller.ConnectedTime+ecx*4], eax |
1479 | bts [esi+usb_controller.NewConnected], ecx |
||
1480 | jmp .nextport |
||
1481 | .disconnect: |
||
1482 | btr [esi+usb_controller.NewConnected], ecx |
||
1483 | jmp .nextport |
||
1484 | .nocsc: |
||
1485 | ; 3i. Handle port disabling. |
||
1486 | ; Note: that needs work. |
||
1487 | test al, 8 |
||
1488 | jz @f |
||
1489 | test al, 4 |
||
1490 | jz @f |
||
1491 | DEBUGF 1,'K : port %d disabled\n',ecx |
||
1492 | @@: |
||
1493 | ; 3j. Continue the loop for the next port. |
||
1494 | .nextport: |
||
1495 | inc ecx |
||
1496 | cmp ecx, [esi+usb_controller.NumPorts] |
||
1497 | jb .portloop |
||
1498 | .skip_roothub: |
||
1499 | ; 4. Process disconnect events. This should be done after step 3 |
||
1500 | ; (which includes the first stage of disconnect processing). |
||
4418 | clevermous | 1501 | invoke usbhc_api.usb_disconnect_stage2 |
3520 | clevermous | 1502 | ; 5. Check for previously connected devices. |
1503 | ; If there is a connected device which was connected less than |
||
1504 | ; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over. |
||
1505 | ; Otherwise, call ehci_new_port. |
||
1506 | ; This should be done after step 3. |
||
1507 | xor ecx, ecx |
||
1508 | cmp [esi+usb_controller.NewConnected], ecx |
||
1509 | jz .skip_newconnected |
||
1510 | .portloop2: |
||
1511 | bt [esi+usb_controller.NewConnected], ecx |
||
1512 | jnc .noconnect |
||
4418 | clevermous | 1513 | invoke GetTimerTicks |
3520 | clevermous | 1514 | sub eax, [esi+usb_controller.ConnectedTime+ecx*4] |
1515 | sub eax, USB_CONNECT_DELAY |
||
1516 | jge .connected |
||
1517 | neg eax |
||
1518 | cmp [esp], eax |
||
1519 | jb .nextport2 |
||
1520 | mov [esp], eax |
||
1521 | jmp .nextport2 |
||
1522 | .connected: |
||
1523 | btr [esi+usb_controller.NewConnected], ecx |
||
1524 | call ehci_new_port |
||
1525 | jmp .portloop2 |
||
1526 | .noconnect: |
||
1527 | .nextport2: |
||
1528 | inc ecx |
||
1529 | cmp ecx, [esi+usb_controller.NumPorts] |
||
1530 | jb .portloop2 |
||
1531 | .skip_newconnected: |
||
1532 | ; 6. Process wait lists. |
||
1533 | ; 6a. Periodic endpoints. |
||
1534 | ; If a request is pending >8 microframes, satisfy it. |
||
1535 | ; If a request is pending <=8 microframes, schedule next wakeup in 0.01s. |
||
1536 | mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] |
||
1537 | cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic] |
||
1538 | jz .noperiodic |
||
1539 | mov edx, [edi+EhciFrameIndexReg] |
||
1540 | sub edx, [esi+usb_controller.StartWaitFrame] |
||
1541 | and edx, 0x3FFF |
||
1542 | cmp edx, 8 |
||
1543 | jbe @f |
||
1544 | mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax |
||
1545 | jmp .noperiodic |
||
1546 | @@: |
||
1547 | pop eax |
||
1548 | push 1 ; wakeup in 0.01 sec for next test |
||
1549 | .noperiodic: |
||
1550 | ; 6b. Asynchronous endpoints. |
||
1551 | ; Satisfy a request when InterruptOnAsyncAdvance fired. |
||
1552 | test byte [esp+4], 20h |
||
1553 | jz @f |
||
4418 | clevermous | 1554 | ; dbgstr 'async advance int' |
3520 | clevermous | 1555 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
1556 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
||
1557 | @@: |
||
1558 | ; Some hardware in some (rarely) conditions set the status bit, |
||
1559 | ; but just does not generate the corresponding interrupt. |
||
1560 | ; Force checking the status here. |
||
1561 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
||
1562 | cmp [esi+usb_controller.ReadyPipeHeadAsync], eax |
||
1563 | jz .noasync |
||
1564 | spin_lock_irq [esi+usb_controller.WaitSpinlock] |
||
1565 | mov edx, [edi+EhciStatusReg] |
||
1566 | test dl, 20h |
||
1567 | jz @f |
||
1568 | mov dword [edi+EhciStatusReg], 20h |
||
1569 | and dword [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], not 20h |
||
1570 | dbgstr 'warning: async advance int missed' |
||
1571 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
||
1572 | jmp .async_unlock |
||
1573 | @@: |
||
1574 | cmp dword [esp], 100 |
||
1575 | jb .async_unlock |
||
1576 | mov dword [esp], 100 |
||
1577 | .async_unlock: |
||
1578 | spin_unlock_irq [esi+usb_controller.WaitSpinlock] |
||
1579 | .noasync: |
||
1580 | ; 7. Finalize transfers processed by hardware. |
||
1581 | ; It is better to perform this step after step 4 (disconnect events), |
||
1582 | ; although not strictly obligatory. This way, an active transfer aborted |
||
1583 | ; due to disconnect would be handled with more specific USB_STATUS_CLOSED, |
||
1584 | ; not USB_STATUS_NORESPONSE. |
||
1585 | test byte [esp+4], 3 |
||
1586 | jz @f |
||
1587 | call ehci_process_updated_schedule |
||
1588 | @@: |
||
1589 | ; 8. Test whether reset signalling has been started and should be stopped now. |
||
1590 | ; This must be done after step 7, because completion of some transfer could |
||
1591 | ; result in resetting a new port. |
||
1592 | .test_reset: |
||
1593 | ; 8a. Test whether reset signalling is active. |
||
1594 | cmp [esi+usb_controller.ResettingStatus], 1 |
||
1595 | jnz .no_reset_in_progress |
||
1596 | ; 8b. Yep. Test whether it should be stopped. |
||
4418 | clevermous | 1597 | invoke GetTimerTicks |
3520 | clevermous | 1598 | sub eax, [esi+usb_controller.ResetTime] |
1599 | sub eax, USB_RESET_TIME |
||
1600 | jge .reset_done |
||
1601 | ; 8c. Not yet, but initiate wakeup in -eax ticks and exit this step. |
||
1602 | neg eax |
||
1603 | cmp [esp], eax |
||
1604 | jb .skip_reset |
||
1605 | mov [esp], eax |
||
1606 | jmp .skip_reset |
||
1607 | .reset_done: |
||
1608 | ; 8d. Yep, call the worker function and proceed to 8e. |
||
1609 | call ehci_port_reset_done |
||
1610 | .no_reset_in_progress: |
||
1611 | ; 8e. Test whether reset process is done, either successful or failed. |
||
1612 | cmp [esi+usb_controller.ResettingStatus], 0 |
||
1613 | jz .skip_reset |
||
1614 | ; 8f. Yep. Test whether it should be stopped. |
||
4418 | clevermous | 1615 | invoke GetTimerTicks |
3520 | clevermous | 1616 | sub eax, [esi+usb_controller.ResetTime] |
1617 | sub eax, USB_RESET_RECOVERY_TIME |
||
1618 | jge .reset_recovery_done |
||
1619 | ; 8g. Not yet, but initiate wakeup in -eax ticks and exit this step. |
||
1620 | neg eax |
||
1621 | cmp [esp], eax |
||
1622 | jb .skip_reset |
||
1623 | mov [esp], eax |
||
1624 | jmp .skip_reset |
||
1625 | .reset_recovery_done: |
||
1626 | ; 8h. Yep, call the worker function. This could initiate another reset, |
||
1627 | ; so return to the beginning of this step. |
||
1628 | call ehci_port_init |
||
1629 | jmp .test_reset |
||
1630 | .skip_reset: |
||
1631 | ; 9. Process wait-done notifications, test for new wait requests. |
||
1632 | ; Note: that must be done after steps 4 and 7 which could create new requests. |
||
1633 | ; 9a. Call the worker function. |
||
4418 | clevermous | 1634 | invoke usbhc_api.usb_process_wait_lists |
3520 | clevermous | 1635 | ; 9b. If it reports that an asynchronous endpoint should be removed, |
1636 | ; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s |
||
1637 | ; (sometimes it just does not fire). |
||
1638 | test al, 1 shl CONTROL_PIPE |
||
1639 | jz @f |
||
1640 | mov edx, [esi+usb_controller.WaitPipeListAsync] |
||
1641 | mov [esi+usb_controller.WaitPipeRequestAsync], edx |
||
1642 | or dword [edi+EhciCommandReg], 1 shl 6 |
||
4418 | clevermous | 1643 | ; dbgstr 'async advance doorbell' |
3520 | clevermous | 1644 | cmp dword [esp], 100 |
1645 | jb @f |
||
1646 | mov dword [esp], 100 |
||
1647 | @@: |
||
1648 | ; 9c. If it reports that a periodic endpoint should be removed, |
||
1649 | ; save the current frame and schedule wakeup in 0.01 sec. |
||
1650 | test al, 1 shl INTERRUPT_PIPE |
||
1651 | jz @f |
||
1652 | mov eax, [esi+usb_controller.WaitPipeListPeriodic] |
||
1653 | mov [esi+usb_controller.WaitPipeRequestPeriodic], eax |
||
1654 | mov edx, [edi+EhciFrameIndexReg] |
||
1655 | mov [esi+usb_controller.StartWaitFrame], edx |
||
1656 | mov dword [esp], 1 ; wakeup in 0.01 sec for next test |
||
1657 | @@: |
||
1658 | ; 10. Pop the return value, restore the stack after step 1 and return. |
||
1659 | pop eax |
||
1660 | pop ecx |
||
1661 | pop edi ebx ; restore used registers to be stdcall |
||
1662 | ret |
||
1663 | endp |
||
1664 | |||
1665 | ; This procedure is called in the USB thread from ehci_process_deferred |
||
1666 | ; when EHCI IRQ handler has signalled that new IOC-packet was processed. |
||
1667 | ; It scans all lists for completed packets and calls ehci_process_finalized_td |
||
1668 | ; for those packets. |
||
1669 | proc ehci_process_updated_schedule |
||
1670 | ; Important note: we cannot hold the list lock during callbacks, |
||
1671 | ; because callbacks sometimes open and/or close pipes and thus acquire/release |
||
1672 | ; the corresponding lock itself. |
||
1673 | ; Fortunately, pipes can be finally freed only by another step of |
||
1674 | ; ehci_process_deferred, so all pipes existing at the start of this function |
||
1675 | ; will be valid while this function is running. Some pipes can be removed |
||
1676 | ; from the corresponding list, some pipes can be inserted; insert/remove |
||
1677 | ; functions guarantee that traversing one list yields all pipes that were in |
||
1678 | ; that list at the beginning of the traversing (possibly with some new pipes, |
||
1679 | ; possibly without some new pipes, that doesn't matter). |
||
1680 | push edi |
||
1681 | ; 1. Process all Periodic lists. |
||
1682 | lea edi, [esi+ehci_controller.IntEDs-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] |
||
1683 | lea ebx, [esi+ehci_controller.IntEDs+63*sizeof.ehci_static_ep-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] |
||
1684 | @@: |
||
1685 | call ehci_process_updated_list |
||
1686 | cmp edi, ebx |
||
1687 | jnz @b |
||
1688 | ; 2. Process the Control list. |
||
1689 | add edi, ehci_controller.ControlDelta |
||
1690 | call ehci_process_updated_list |
||
1691 | ; 3. Process the Bulk list. |
||
1692 | call ehci_process_updated_list |
||
1693 | ; 4. Return. |
||
1694 | pop edi |
||
1695 | ret |
||
1696 | endp |
||
1697 | |||
1698 | ; This procedure is called from ehci_process_updated_schedule, see comments there. |
||
1699 | ; It processes one list, esi -> usb_controller, edi -> usb_static_ep, |
||
1700 | ; and advances edi to next head. |
||
1701 | proc ehci_process_updated_list |
||
1702 | push ebx |
||
1703 | ; 1. Perform the external loop over all pipes. |
||
1704 | mov ebx, [edi+usb_static_ep.NextVirt] |
||
1705 | .loop: |
||
1706 | cmp ebx, edi |
||
1707 | jz .done |
||
1708 | ; store pointer to the next pipe in the stack |
||
1709 | push [ebx+usb_static_ep.NextVirt] |
||
1710 | ; 2. For every pipe, perform the internal loop over all descriptors. |
||
1711 | ; All descriptors are organized in the queue; we process items from the start |
||
1712 | ; of the queue until a) the last descriptor (not the part of the queue itself) |
||
1713 | ; or b) an active (not yet processed by the hardware) descriptor is reached. |
||
1714 | lea ecx, [ebx+usb_pipe.Lock] |
||
4418 | clevermous | 1715 | invoke MutexLock |
3520 | clevermous | 1716 | mov ebx, [ebx+usb_pipe.LastTD] |
1717 | push ebx |
||
1718 | mov ebx, [ebx+usb_gtd.NextVirt] |
||
1719 | .tdloop: |
||
1720 | ; 3. For every descriptor, test active flag and check for end-of-queue; |
||
1721 | ; if either of conditions holds, exit from the internal loop. |
||
1722 | cmp ebx, [esp] |
||
1723 | jz .tddone |
||
3653 | clevermous | 1724 | cmp byte [ebx+ehci_gtd.Token-sizeof.ehci_gtd], 0 |
3520 | clevermous | 1725 | js .tddone |
1726 | ; Release the queue lock while processing one descriptor: |
||
1727 | ; callback function could (and often would) schedule another transfer. |
||
1728 | push ecx |
||
4418 | clevermous | 1729 | invoke MutexUnlock |
3520 | clevermous | 1730 | call ehci_process_updated_td |
1731 | pop ecx |
||
4418 | clevermous | 1732 | invoke MutexLock |
3520 | clevermous | 1733 | jmp .tdloop |
1734 | .tddone: |
||
4418 | clevermous | 1735 | invoke MutexUnlock |
3520 | clevermous | 1736 | pop ebx |
1737 | ; End of internal loop, restore pointer to the next pipe |
||
1738 | ; and continue the external loop. |
||
1739 | pop ebx |
||
1740 | jmp .loop |
||
1741 | .done: |
||
1742 | pop ebx |
||
1743 | add edi, sizeof.ehci_static_ep |
||
1744 | ret |
||
1745 | endp |
||
1746 | |||
1747 | ; This procedure is called from ehci_process_updated_list, which is itself |
||
1748 | ; called from ehci_process_updated_schedule, see comments there. |
||
1749 | ; It processes one completed descriptor. |
||
1750 | ; in: ebx -> usb_gtd, out: ebx -> next usb_gtd. |
||
1751 | proc ehci_process_updated_td |
||
1752 | ; mov eax, [ebx+usb_gtd.Pipe] |
||
1753 | ; cmp [eax+usb_pipe.Type], INTERRUPT_PIPE |
||
1754 | ; jnz @f |
||
1755 | ; DEBUGF 1,'K : finalized TD for pipe %x:\n',eax |
||
3653 | clevermous | 1756 | ; lea eax, [ebx-sizeof.ehci_gtd] |
3520 | clevermous | 1757 | ; DEBUGF 1,'K : %x %x %x %x\n',[eax],[eax+4],[eax+8],[eax+12] |
1758 | ; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28] |
||
1759 | ;@@: |
||
1760 | ; 1. Remove this descriptor from the list of descriptors for this pipe. |
||
4418 | clevermous | 1761 | invoke usbhc_api.usb_unlink_td |
3520 | clevermous | 1762 | ; 2. Calculate actual number of bytes transferred. |
3653 | clevermous | 1763 | mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] |
3520 | clevermous | 1764 | lea edx, [eax+eax] |
1765 | shr edx, 17 |
||
1766 | sub edx, [ebx+usb_gtd.Length] |
||
1767 | neg edx |
||
1768 | ; 3. Check whether we need some special processing beyond notifying the driver. |
||
1769 | ; Transfer errors require special processing. |
||
1770 | ; Short packets require special processing if |
||
1771 | ; a) this is not the last descriptor for transfer stage |
||
1772 | ; (in this case we need to process subsequent descriptors for the stage too) |
||
1773 | ; or b) the caller considers short transfers to be an error. |
||
1774 | ; ehci_alloc_transfer sets bit 0 of ehci_gtd.Flags to 0 if short packet |
||
1775 | ; in this descriptor requires special processing and to 1 otherwise. |
||
1776 | ; If special processing is not needed, advance to 4 with ecx = 0. |
||
1777 | ; Otherwise, go to 6. |
||
1778 | xor ecx, ecx |
||
1779 | test al, 40h |
||
1780 | jnz .error |
||
3653 | clevermous | 1781 | test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
3520 | clevermous | 1782 | jnz .notify |
1783 | cmp edx, [ebx+usb_gtd.Length] |
||
1784 | jnz .special |
||
1785 | .notify: |
||
1786 | ; 4. Either the descriptor in ebx was processed without errors, |
||
1787 | ; or all necessary error actions were taken and ebx points to the last |
||
1788 | ; related descriptor. |
||
4418 | clevermous | 1789 | invoke usbhc_api.usb_process_gtd |
3520 | clevermous | 1790 | ; 5. Free the current descriptor and return the next one. |
1791 | push [ebx+usb_gtd.NextVirt] |
||
1792 | stdcall ehci_free_td, ebx |
||
1793 | pop ebx |
||
1794 | ret |
||
1795 | .error: |
||
1796 | push ebx |
||
3653 | clevermous | 1797 | sub ebx, sizeof.ehci_gtd |
3520 | clevermous | 1798 | DEBUGF 1,'K : TD failed:\n' |
1799 | DEBUGF 1,'K : %x %x %x %x\n',[ebx],[ebx+4],[ebx+8],[ebx+12] |
||
1800 | DEBUGF 1,'K : %x %x %x %x\n',[ebx+16],[ebx+20],[ebx+24],[ebx+28] |
||
1801 | pop ebx |
||
1802 | DEBUGF 1,'K : pipe now:\n' |
||
1803 | mov ecx, [ebx+usb_gtd.Pipe] |
||
3653 | clevermous | 1804 | sub ecx, sizeof.ehci_pipe |
3520 | clevermous | 1805 | DEBUGF 1,'K : %x %x %x %x\n',[ecx],[ecx+4],[ecx+8],[ecx+12] |
1806 | DEBUGF 1,'K : %x %x %x %x\n',[ecx+16],[ecx+20],[ecx+24],[ecx+28] |
||
1807 | DEBUGF 1,'K : %x %x %x %x\n',[ecx+32],[ecx+36],[ecx+40],[ecx+44] |
||
1808 | .special: |
||
1809 | ; 6. Special processing is needed. |
||
1810 | ; 6a. Save the status and length. |
||
1811 | push edx |
||
1812 | push eax |
||
1813 | ; 6b. Traverse the list of descriptors looking for the final descriptor |
||
1814 | ; for this transfer. Free and unlink non-final descriptors. |
||
1815 | ; Final descriptor will be freed in step 5. |
||
1816 | .look_final: |
||
4418 | clevermous | 1817 | invoke usbhc_api.usb_is_final_packet |
3520 | clevermous | 1818 | jnc .found_final |
1819 | push [ebx+usb_gtd.NextVirt] |
||
1820 | stdcall ehci_free_td, ebx |
||
1821 | pop ebx |
||
4418 | clevermous | 1822 | invoke usbhc_api.usb_unlink_td |
3520 | clevermous | 1823 | jmp .look_final |
1824 | .found_final: |
||
1825 | ; 6c. Restore the status saved in 6a and transform it to the error code. |
||
1826 | ; Notes: |
||
1827 | ; * any USB transaction error results in Halted bit; if it is not set, |
||
1828 | ; but we are here, it must be due to short packet; |
||
1829 | ; * babble is considered a fatal USB transaction error, |
||
1830 | ; other errors just lead to retrying the transaction; |
||
1831 | ; if babble is detected, return the corresponding error; |
||
1832 | ; * if several non-fatal errors have occured during transaction retries, |
||
1833 | ; all corresponding bits are set. In this case, return some error code, |
||
1834 | ; the order is quite arbitrary. |
||
1835 | pop eax ; status |
||
3598 | clevermous | 1836 | movi ecx, USB_STATUS_UNDERRUN |
3520 | clevermous | 1837 | test al, 40h ; not Halted? |
1838 | jz .know_error |
||
1839 | mov cl, USB_STATUS_OVERRUN |
||
1840 | test al, 10h ; Babble detected? |
||
1841 | jnz .know_error |
||
1842 | mov cl, USB_STATUS_BUFOVERRUN |
||
1843 | test al, 20h ; Data Buffer error? |
||
1844 | jnz .know_error |
||
1845 | mov cl, USB_STATUS_NORESPONSE |
||
1846 | test al, 8 ; Transaction Error? |
||
1847 | jnz .know_error |
||
1848 | mov cl, USB_STATUS_STALL |
||
1849 | .know_error: |
||
1850 | ; 6d. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets, |
||
1851 | ; it is not an error; in this case, go to 4 with ecx = 0. |
||
1852 | cmp ecx, USB_STATUS_UNDERRUN |
||
1853 | jnz @f |
||
3653 | clevermous | 1854 | test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
3520 | clevermous | 1855 | jz @f |
1856 | xor ecx, ecx |
||
1857 | pop edx ; length |
||
1858 | jmp .notify |
||
1859 | @@: |
||
1860 | ; 6e. Abort the entire transfer. |
||
1861 | ; There are two cases: either there is only one transfer stage |
||
1862 | ; (everything except control transfers), then ebx points to the last TD and |
||
1863 | ; all previous TD were unlinked and dismissed (if possible), |
||
1864 | ; or there are several stages (a control transfer) and ebx points to the last |
||
1865 | ; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, |
||
1866 | ; because Setup stage can not produce short packets); for Data stage, we need |
||
1867 | ; to unlink and free (if possible) one more TD and advance ebx to the next one. |
||
1868 | cmp [ebx+usb_gtd.Callback], 0 |
||
1869 | jnz .normal |
||
1870 | push ecx |
||
1871 | push [ebx+usb_gtd.NextVirt] |
||
1872 | stdcall ehci_free_td, ebx |
||
1873 | pop ebx |
||
4418 | clevermous | 1874 | invoke usbhc_api.usb_unlink_td |
3520 | clevermous | 1875 | pop ecx |
1876 | .normal: |
||
1877 | ; 6f. For bulk/interrupt transfers we have no choice but halt the queue, |
||
1878 | ; the driver should intercede (through some API which is not written yet). |
||
1879 | ; Control pipes normally recover at the next SETUP transaction (first stage |
||
1880 | ; of any control transfer), so we hope on the best and just advance the queue |
||
1881 | ; to the next transfer. (According to the standard, "A control pipe may also |
||
1882 | ; support functional stall as well, but this is not recommended."). |
||
1883 | mov edx, [ebx+usb_gtd.Pipe] |
||
3653 | clevermous | 1884 | mov eax, [ebx+ehci_gtd.NextTD-sizeof.ehci_gtd] |
3520 | clevermous | 1885 | or al, 1 |
3653 | clevermous | 1886 | mov [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
1887 | mov [edx+ehci_pipe.Overlay.AlternateNextTD-sizeof.ehci_pipe], eax |
||
3520 | clevermous | 1888 | cmp [edx+usb_pipe.Type], CONTROL_PIPE |
1889 | jz .control |
||
1890 | ; Bulk/interrupt transfer; halt the queue. |
||
3653 | clevermous | 1891 | mov [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 40h |
3520 | clevermous | 1892 | pop edx |
1893 | jmp .notify |
||
1894 | ; Control transfer. |
||
1895 | .control: |
||
3653 | clevermous | 1896 | and [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 0 |
1897 | dec [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe] |
||
3520 | clevermous | 1898 | pop edx |
1899 | jmp .notify |
||
1900 | endp |
||
1901 | |||
1902 | ; This procedure unlinks the pipe from the corresponding pipe list. |
||
1903 | ; esi -> usb_controller, ebx -> usb_pipe |
||
1904 | proc ehci_unlink_pipe |
||
1905 | cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE |
||
1906 | jnz @f |
||
3653 | clevermous | 1907 | test word [ebx+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
3520 | clevermous | 1908 | jnz .interrupt_fs |
1909 | call ehci_hs_interrupt_list_unlink |
||
1910 | jmp .interrupt_common |
||
1911 | .interrupt_fs: |
||
1912 | call ehci_fs_interrupt_list_unlink |
||
1913 | .interrupt_common: |
||
1914 | @@: |
||
1915 | mov edx, [ebx+usb_pipe.NextVirt] |
||
1916 | mov eax, [ebx+usb_pipe.PrevVirt] |
||
1917 | mov [edx+usb_pipe.PrevVirt], eax |
||
1918 | mov [eax+usb_pipe.NextVirt], edx |
||
1919 | mov edx, esi |
||
1920 | sub edx, eax |
||
1921 | cmp edx, sizeof.ehci_controller |
||
3653 | clevermous | 1922 | mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe] |
3520 | clevermous | 1923 | jb .prev_is_static |
3653 | clevermous | 1924 | mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx |
3520 | clevermous | 1925 | ret |
1926 | .prev_is_static: |
||
1927 | mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx |
||
1928 | ret |
||
1929 | endp |
||
1930 | |||
1931 | proc ehci_alloc_td |
||
1932 | push ebx |
||
1933 | mov ebx, ehci_gtd_mutex |
||
4418 | clevermous | 1934 | invoke usbhc_api.usb_allocate_common, (sizeof.ehci_gtd + sizeof.usb_gtd + 1Fh) and not 1Fh |
3520 | clevermous | 1935 | test eax, eax |
1936 | jz @f |
||
3653 | clevermous | 1937 | add eax, sizeof.ehci_gtd |
3520 | clevermous | 1938 | @@: |
1939 | pop ebx |
||
1940 | ret |
||
1941 | endp |
||
1942 | |||
1943 | ; This procedure is called from several places from main USB code and |
||
1944 | ; frees all additional data associated with the transfer descriptor. |
||
1945 | ; EHCI has no additional data, so just free ehci_gtd structure. |
||
1946 | proc ehci_free_td |
||
3653 | clevermous | 1947 | sub dword [esp+4], sizeof.ehci_gtd |
4418 | clevermous | 1948 | jmp [usbhc_api.usb_free_common] |
3520 | clevermous | 1949 | endp |
4418 | clevermous | 1950 | |
1951 | include 'ehci_scheduler.inc' |
||
1952 | |||
1953 | section '.data' readable writable |
||
1954 | include '../peimport.inc' |
||
1955 | include_debug_strings |
||
1956 | IncludeIGlobals |
||
1957 | IncludeUGlobals |
||
1958 | align 4 |
||
1959 | usbhc_api usbhc_func=8>>>>>>>>>=>=> |