Rev 4418 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3520 | clevermous | 1 | ; Code for UHCI 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 | ; UHCI register declarations |
||
20 | UhciCommandReg = 0 |
||
21 | UhciStatusReg = 2 |
||
22 | UhciInterruptReg = 4 |
||
23 | UhciFrameNumberReg = 6 |
||
24 | UhciBaseAddressReg = 8 |
||
25 | UhciSOFModifyReg = 0Ch |
||
26 | UhciPort1StatusReg = 10h |
||
27 | ; possible PIDs for USB data transfers |
||
28 | USB_PID_SETUP = 2Dh |
||
29 | USB_PID_IN = 69h |
||
30 | USB_PID_OUT = 0E1h |
||
31 | ; UHCI does not support an interrupt on root hub status change. We must poll |
||
32 | ; the controller periodically. This is the period in timer ticks (10ms). |
||
3657 | clevermous | 33 | ; We use the value 100 ticks: it is small enough to be responsive to connect |
3656 | clevermous | 34 | ; events and large enough to not load CPU too often. |
3520 | clevermous | 35 | UHCI_POLL_INTERVAL = 100 |
36 | ; the following constant is an invalid encoding for length fields in |
||
37 | ; uhci_gtd; it is used to check whether an inactive TD has been |
||
38 | ; completed (actual length of the transfer is valid) or not processed at all |
||
39 | ; (actual length of the transfer is UHCI_INVALID_LENGTH). |
||
40 | ; Valid values are 0-4FFh and 7FFh. We use 700h as an invalid value. |
||
41 | UHCI_INVALID_LENGTH = 700h |
||
42 | |||
43 | ; ============================================================================= |
||
44 | ; ================================ Structures ================================= |
||
45 | ; ============================================================================= |
||
46 | |||
47 | ; UHCI-specific part of a pipe descriptor. |
||
48 | ; * The structure corresponds to the Queue Head aka QH from the UHCI |
||
49 | ; specification with some additional fields. |
||
50 | ; * The hardware uses first two fields (8 bytes). Next two fields are used for |
||
51 | ; software book-keeping. |
||
52 | ; * The hardware requires 16-bytes alignment of the hardware part. |
||
53 | ; Since the allocator (usb_allocate_common) allocates memory sequentially |
||
3653 | clevermous | 54 | ; from page start (aligned on 0x1000 bytes), block size for the allocator |
55 | ; must be divisible by 16; usb1_allocate_endpoint ensures this. |
||
3520 | clevermous | 56 | struct uhci_pipe |
57 | NextQH dd ? |
||
58 | ; 1. First bit (bit 0) is Terminate bit. 1 = there is no next QH. |
||
59 | ; 2. Next bit (bit 1) is QH/TD select bit. 1 = NextQH points to QH. |
||
60 | ; 3. Next two bits (bits 2-3) are reserved. |
||
61 | ; 4. With masked 4 lower bits, this is the physical address of the next QH in |
||
62 | ; the QH list. |
||
63 | ; See also the description before NextVirt field of the usb_pipe |
||
64 | ; structure. Additionally to that description, the following is specific for |
||
65 | ; the UHCI controller: |
||
66 | ; * n=10, N=1024. However, this number is quite large. |
||
67 | ; * 1024 lists are used only for individual transfer descriptors for |
||
68 | ; Isochronous endpoints. This means that the software can sleep up to 1024 ms |
||
69 | ; before initiating the next portion of a large isochronous transfer, which |
||
70 | ; is a sufficiently large value. |
||
71 | ; * We use the 32ms upper limit for interrupt endpoint polling interval. |
||
72 | ; This seems to be a reasonable value. |
||
73 | ; * The "next" list for last Periodic list is the Control list. |
||
74 | ; * The "next" list for Control list is Bulk list and the "next" |
||
75 | ; list for Bulk list is Control list. This loop is used for bandwidth |
||
76 | ; reclamation: the hardware traverses lists until end-of-frame. |
||
77 | HeadTD dd ? |
||
78 | ; 1. First bit (bit 0) is Terminate bit. 1 = there is no TDs in this QH. |
||
79 | ; 2. Next bit (bit 1) is QH/TD select bit. 1 = HeadTD points to QH. |
||
80 | ; 3. Next two bits (bits 2-3) are reserved. |
||
81 | ; 4. With masked 4 lower bits, this is the physical address of the first TD in |
||
82 | ; the TD queue for this QH. |
||
83 | Token dd ? |
||
84 | ; This field is a template for uhci_gtd.Token field in transfer |
||
85 | ; descriptors. The meaning of individual bits is the same as for |
||
86 | ; uhci_gtd.Token, except that PID bitfield is always |
||
87 | ; USB_PID_SETUP/IN/OUT for control/in/out pipes, |
||
88 | ; the MaximumLength bitfield encodes maximum packet size, |
||
89 | ; the Reserved bit 20 is LowSpeedDevice bit. |
||
90 | ErrorTD dd ? |
||
91 | ; Usually NULL. If nonzero, it is a pointer to descriptor which was error'd |
||
92 | ; and should be freed sometime in the future (the hardware could still use it). |
||
93 | ends |
||
94 | |||
95 | ; This structure describes the static head of every list of pipes. |
||
96 | ; The hardware requires 16-bytes alignment of this structure. |
||
97 | ; All instances of this structure are located sequentially in uhci_controller, |
||
98 | ; uhci_controller is page-aligned, so it is sufficient to make this structure |
||
99 | ; 16-bytes aligned and verify that the first instance is 16-bytes aligned |
||
100 | ; inside uhci_controller. |
||
101 | struct uhci_static_ep |
||
102 | NextQH dd ? |
||
103 | ; Same as uhci_pipe.NextQH. |
||
104 | HeadTD dd ? |
||
105 | ; Same as uhci_pipe.HeadTD. |
||
106 | NextList dd ? |
||
107 | ; Virtual address of the next list. |
||
108 | dd ? |
||
109 | ; Not used. |
||
110 | SoftwarePart rd sizeof.usb_static_ep/4 |
||
111 | ; Common part for all controllers, described by usb_static_ep structure. |
||
112 | dd ? |
||
113 | ; Padding for 16-byte alignment. |
||
114 | ends |
||
115 | |||
116 | if sizeof.uhci_static_ep mod 16 |
||
117 | .err uhci_static_ep must be 16-bytes aligned |
||
118 | end if |
||
119 | |||
120 | ; UHCI-specific part of controller data. |
||
121 | ; * The structure includes two parts, the hardware part and the software part. |
||
122 | ; * The hardware part consists of first 4096 bytes and corresponds to |
||
123 | ; the Frame List from UHCI specification. |
||
124 | ; * The hardware requires page-alignment of the hardware part, so |
||
125 | ; the entire descriptor must be page-aligned. |
||
126 | ; This structure is allocated with kernel_alloc (see usb_init_controller), |
||
127 | ; this gives page-aligned data. |
||
128 | struct uhci_controller |
||
129 | ; ------------------------------ hardware fields ------------------------------ |
||
130 | FrameList rd 1024 |
||
131 | ; Entry n corresponds to the head of the frame list to be executed in |
||
3579 | clevermous | 132 | ; the frames n,n+1024,n+2048,n+3072,... |
3520 | clevermous | 133 | ; The first bit of each entry is Terminate bit, 1 = the frame is empty. |
134 | ; The second bit of each entry is QH/TD select bit, 1 = the entry points to |
||
135 | ; QH, 0 = to TD. |
||
136 | ; With masked 2 lower bits, the entry is a physical address of the first QH/TD |
||
137 | ; to be executed. |
||
138 | ; ------------------------------ software fields ------------------------------ |
||
139 | ; Every list has the static head, which is an always empty QH. |
||
140 | ; The following fields are static heads, one per list: |
||
141 | ; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list. |
||
142 | IntEDs uhci_static_ep |
||
143 | rb 62 * sizeof.uhci_static_ep |
||
144 | ControlED uhci_static_ep |
||
145 | BulkED uhci_static_ep |
||
146 | IOBase dd ? |
||
147 | ; Base port in I/O space for UHCI controller. |
||
148 | ; UHCI register UhciXxx is addressed as in/out to IOBase + UhciXxx, |
||
149 | ; see declarations in the beginning of this source. |
||
150 | DeferredActions dd ? |
||
151 | ; Bitmask of bits from UhciStatusReg which need to be processed |
||
152 | ; by uhci_process_deferred. Bit 0 = a transaction with IOC bit |
||
153 | ; has completed. Bit 1 = a transaction has failed. Set by uhci_irq, |
||
154 | ; cleared by uhci_process_deferred. |
||
155 | LastPollTime dd ? |
||
156 | ; See the comment before UHCI_POLL_INTERVAL. This variable keeps the |
||
157 | ; last time, in timer ticks, when the polling was done. |
||
4418 | clevermous | 158 | EhciCompanion dd ? |
159 | ; Pointer to usb_controller for EHCI companion, if any, or NULL. |
||
3520 | clevermous | 160 | ends |
161 | |||
162 | if uhci_controller.IntEDs mod 16 |
||
163 | .err Static endpoint descriptors must be 16-bytes aligned inside uhci_controller |
||
164 | end if |
||
165 | |||
166 | ; UHCI general transfer descriptor. |
||
167 | ; * The structure describes non-Isochronous data transfers |
||
168 | ; for the UHCI controller. |
||
169 | ; * The structure includes two parts, the hardware part and the software part. |
||
170 | ; * The hardware part consists of first 16 bytes and corresponds to the |
||
171 | ; Transfer Descriptor aka TD from UHCI specification. |
||
172 | ; * The hardware requires 16-bytes alignment of the hardware part, so |
||
173 | ; the entire descriptor must be 16-bytes aligned. Since the allocator |
||
174 | ; (uhci_allocate_common) allocates memory sequentially from page start |
||
3653 | clevermous | 175 | ; (aligned on 0x1000 bytes), block size for the allocator must be |
176 | ; divisible by 16; usb1_allocate_general_td ensures this. |
||
3520 | clevermous | 177 | struct uhci_gtd |
178 | NextTD dd ? |
||
179 | ; 1. First bit (bit 0) is Terminate bit. 1 = there is no next TD. |
||
180 | ; 2. Next bit (bit 1) is QH/TD select bit. 1 = NextTD points to QH. |
||
181 | ; This bit is always set to 0 in the implementation. |
||
182 | ; 3. Next bit (bit 2) is Depth/Breadth select bit. 1 = the controller should |
||
183 | ; proceed to the NextTD after this TD is complete. 0 = the controller |
||
184 | ; should proceed to the next endpoint after this TD is complete. |
||
185 | ; The implementation sets this bit to 0 for final stages of all transactions |
||
186 | ; and to 1 for other stages. |
||
187 | ; 4. Next bit (bit 3) is reserved and must be zero. |
||
188 | ; 5. With masked 4 lower bits, this is the physical address of the next TD |
||
189 | ; in the TD list. |
||
190 | ControlStatus dd ? |
||
191 | ; 1. Lower 11 bits (bits 0-10) are ActLen. This is written by the controller |
||
192 | ; at the conclusion of a USB transaction to indicate the actual number of |
||
193 | ; bytes that were transferred minus 1. |
||
194 | ; 2. Next 6 bits (bits 11-16) are reserved. |
||
195 | ; 3. Next bit (bit 17) signals Bitstuff error. |
||
196 | ; 4. Next bit (bit 18) signals CRC/Timeout error. |
||
197 | ; 5. Next bit (bit 19) signals NAK receive. |
||
198 | ; 6. Next bit (bit 20) signals Babble error. |
||
199 | ; 7. Next bit (bit 21) signals Data Buffer error. |
||
200 | ; 8. Next bit (bit 22) signals Stall error. |
||
201 | ; 9. Next bit (bit 23) is Active field. 1 = this TD should be processed. |
||
202 | ; 10. Next bit (bit 24) is InterruptOnComplete bit. 1 = the controller should |
||
203 | ; issue an interrupt on completion of the frame in which this TD is |
||
204 | ; executed. |
||
205 | ; 11. Next bit (bit 25) is IsochronousSelect bit. 1 = this TD is isochronous. |
||
206 | ; 12. Next bit (bit 26) is LowSpeedDevice bit. 1 = this TD is for low-speed. |
||
207 | ; 13. Next two bits (bits 27-28) are ErrorCounter field. This field is |
||
208 | ; decremented by the controller on every non-fatal error with this TD. |
||
209 | ; Babble and Stall are considered fatal errors and immediately deactivate |
||
210 | ; the TD without decrementing this field. 0 = no error limit, |
||
211 | ; n = deactivate the TD after n errors. |
||
212 | ; 14. Next bit (bit 29) is ShortPacketDetect bit. 1 = short packet is an error. |
||
213 | ; Note: the specification defines this bit as input for the controller, |
||
214 | ; but does not specify the value written by controller. |
||
215 | ; Some controllers (e.g. Intel) keep the value, some controllers (e.g. VIA) |
||
216 | ; set the value to whether a short packet was actually detected |
||
217 | ; (or something like that). |
||
218 | ; Thus, we duplicate this bit as bit 0 of OrigBufferInfo. |
||
219 | ; 15. Upper two bits (bits 30-31) are reserved. |
||
220 | Token dd ? |
||
221 | ; 1. Lower 8 bits (bits 0-7) are PID, one of USB_PID_*. |
||
222 | ; 2. Next 7 bits (bits 8-14) are DeviceAddress field. This is the address of |
||
223 | ; the target device on the USB bus. |
||
224 | ; 3. Next 4 bits (bits 15-18) are Endpoint field. This is the target endpoint |
||
225 | ; number. |
||
226 | ; 4. Next bit (bit 19) is DataToggle bit. n = issue/expect DATAn token. |
||
227 | ; 5. Next bit (bit 20) is reserved. |
||
228 | ; 6. Upper 11 bits (bits 21-31) are MaximumLength field. This field specifies |
||
229 | ; the maximum number of data bytes for the transfer minus 1 byte. Null data |
||
230 | ; packet is encoded as 0x7FF, maximum possible non-null data packet is 1280 |
||
231 | ; bytes, encoded as 0x4FF. |
||
232 | Buffer dd ? |
||
233 | ; Physical address of the data buffer for this TD. |
||
234 | OrigBufferInfo dd ? |
||
235 | ; Usually NULL. If the original buffer crosses a page boundary, this is a |
||
236 | ; pointer to the structure uhci_original_buffer for this request. |
||
237 | ; bit 0: 1 = short packet is NOT allowed |
||
238 | ; (before the TD is processed, it is the copy of bit 29 of ControlStatus; |
||
239 | ; some controllers modify that bit, so we need a copy in a safe place) |
||
240 | ends |
||
241 | |||
242 | ; UHCI requires that the entire transfer buffer should be on one page. |
||
243 | ; If the actual buffer crosses page boundary, uhci_alloc_packet |
||
244 | ; allocates additional memory for buffer for hardware. |
||
245 | ; This structure describes correspondence between two buffers. |
||
246 | struct uhci_original_buffer |
||
247 | OrigBuffer dd ? |
||
248 | UsedBuffer dd ? |
||
249 | ends |
||
250 | |||
251 | ; Description of UHCI-specific data and functions for |
||
252 | ; controller-independent code. |
||
253 | ; Implements the structure usb_hardware_func from hccommon.inc for UHCI. |
||
254 | iglobal |
||
255 | align 4 |
||
256 | uhci_hardware_func: |
||
4418 | clevermous | 257 | dd USBHC_VERSION |
3520 | clevermous | 258 | dd 'UHCI' |
259 | dd sizeof.uhci_controller |
||
4418 | clevermous | 260 | dd uhci_kickoff_bios |
3520 | clevermous | 261 | dd uhci_init |
262 | dd uhci_process_deferred |
||
263 | dd uhci_set_device_address |
||
264 | dd uhci_get_device_address |
||
265 | dd uhci_port_disable |
||
266 | dd uhci_new_port.reset |
||
267 | dd uhci_set_endpoint_packet_size |
||
4418 | clevermous | 268 | dd uhci_alloc_pipe |
3520 | clevermous | 269 | dd uhci_free_pipe |
270 | dd uhci_init_pipe |
||
271 | dd uhci_unlink_pipe |
||
4418 | clevermous | 272 | dd uhci_alloc_td |
3520 | clevermous | 273 | dd uhci_free_td |
274 | dd uhci_alloc_transfer |
||
275 | dd uhci_insert_transfer |
||
276 | dd uhci_new_device |
||
4547 | clevermous | 277 | dd uhci_disable_pipe |
278 | dd uhci_enable_pipe |
||
4418 | clevermous | 279 | uhci_name db 'UHCI',0 |
3520 | clevermous | 280 | endg |
281 | |||
282 | ; ============================================================================= |
||
283 | ; =================================== Code ==================================== |
||
284 | ; ============================================================================= |
||
285 | |||
4418 | clevermous | 286 | ; Called once when driver is loading and once at shutdown. |
287 | ; When loading, must initialize itself, register itself in the system |
||
288 | ; and return eax = value obtained when registering. |
||
289 | proc start |
||
290 | virtual at esp |
||
291 | dd ? ; return address |
||
292 | .reason dd ? ; DRV_ENTRY or DRV_EXIT |
||
293 | .cmdline dd ? ; normally NULL |
||
294 | end virtual |
||
295 | cmp [.reason], DRV_ENTRY |
||
296 | jnz .nothing |
||
297 | mov ecx, uhci_ep_mutex |
||
298 | and dword [ecx-4], 0 |
||
299 | invoke MutexInit |
||
300 | mov ecx, uhci_gtd_mutex |
||
301 | and dword [ecx-4], 0 |
||
302 | invoke MutexInit |
||
303 | push esi edi |
||
304 | mov esi, [USBHCFunc] |
||
305 | mov edi, usbhc_api |
||
306 | movi ecx, sizeof.usbhc_func/4 |
||
307 | rep movsd |
||
308 | pop edi esi |
||
309 | invoke RegUSBDriver, uhci_name, 0, uhci_hardware_func |
||
310 | .nothing: |
||
311 | ret |
||
312 | endp |
||
313 | |||
3520 | clevermous | 314 | ; Controller-specific initialization function. |
315 | ; Called from usb_init_controller. Initializes the hardware and |
||
316 | ; UHCI-specific parts of software structures. |
||
317 | ; eax = pointer to uhci_controller to be initialized |
||
318 | ; [ebp-4] = pcidevice |
||
319 | proc uhci_init |
||
320 | ; inherit some variables from the parent (usb_init_controller) |
||
321 | .devfn equ ebp - 4 |
||
322 | .bus equ ebp - 3 |
||
323 | ; 1. Store pointer to uhci_controller for further use. |
||
324 | push eax |
||
325 | mov edi, eax |
||
326 | mov esi, eax |
||
327 | ; 2. Initialize uhci_controller.FrameList. |
||
328 | ; Note that FrameList is located in the beginning of uhci_controller, |
||
329 | ; so esi and edi now point to uhci_controller.FrameList. |
||
330 | ; First 32 entries of FrameList contain physical addresses |
||
331 | ; of first 32 Periodic static heads, further entries duplicate these. |
||
332 | ; See the description of structures for full info. |
||
333 | ; Note that all static heads fit in one page, so one call to |
||
334 | ; get_phys_addr is sufficient. |
||
335 | if (uhci_controller.IntEDs / 0x1000) <> (uhci_controller.BulkED / 0x1000) |
||
336 | .err assertion failed |
||
337 | end if |
||
338 | ; 2a. Get physical address of first static head. |
||
339 | ; Note that 1) it is located in the beginning of a page |
||
340 | ; and 2) all other static heads fit in the same page, |
||
341 | ; so one call to get_phys_addr without correction of lower 12 bits |
||
342 | ; is sufficient. |
||
343 | if (uhci_controller.IntEDs mod 0x1000) <> 0 |
||
344 | .err assertion failed |
||
345 | end if |
||
346 | add eax, uhci_controller.IntEDs |
||
4418 | clevermous | 347 | invoke GetPhysAddr |
3520 | clevermous | 348 | ; 2b. Fill first 32 entries. |
349 | inc eax |
||
350 | inc eax ; set QH bit for uhci_pipe.NextQH |
||
3598 | clevermous | 351 | movi ecx, 32 |
3520 | clevermous | 352 | mov edx, ecx |
353 | @@: |
||
354 | stosd |
||
355 | add eax, sizeof.uhci_static_ep |
||
356 | loop @b |
||
357 | ; 2c. Fill the rest entries. |
||
358 | mov ecx, 1024 - 32 |
||
359 | rep movsd |
||
360 | ; 3. Initialize static heads uhci_controller.*ED. |
||
361 | ; Use the loop over groups: first group consists of first 32 Periodic |
||
362 | ; descriptors, next group consists of next 16 Periodic descriptors, |
||
363 | ; ..., last group consists of the last Periodic descriptor. |
||
364 | ; 3a. Prepare for the loop. |
||
365 | ; make esi point to the second group, other registers are already set. |
||
366 | add esi, 32*4 + 32*sizeof.uhci_static_ep |
||
367 | ; 3b. Loop over groups. On every iteration: |
||
368 | ; edx = size of group, edi = pointer to the current group, |
||
369 | ; esi = pointer to the next group, eax = physical address of the next group. |
||
370 | .init_static_eds: |
||
371 | ; 3c. Get the size of next group. |
||
372 | shr edx, 1 |
||
373 | ; 3d. Exit the loop if there is no next group. |
||
374 | jz .init_static_eds_done |
||
375 | ; 3e. Initialize the first half of the current group. |
||
376 | ; Advance edi to the second half. |
||
377 | push eax esi |
||
378 | call uhci_init_static_ep_group |
||
379 | pop esi eax |
||
380 | ; 3f. Initialize the second half of the current group |
||
381 | ; with the same values. |
||
382 | ; Advance edi to the next group, esi/eax to the next of the next group. |
||
383 | call uhci_init_static_ep_group |
||
384 | jmp .init_static_eds |
||
385 | .init_static_eds_done: |
||
386 | ; 3g. Initialize the last static head. |
||
387 | xor esi, esi |
||
388 | call uhci_init_static_endpoint |
||
389 | ; 3i. Initialize the head of Control list. |
||
390 | add eax, sizeof.uhci_static_ep |
||
391 | call uhci_init_static_endpoint |
||
392 | ; 3j. Initialize the head of Bulk list. |
||
393 | sub eax, sizeof.uhci_static_ep |
||
394 | call uhci_init_static_endpoint |
||
395 | ; 4. Get I/O base address and size from PCI bus. |
||
396 | ; 4a. Read&save PCI command state. |
||
4418 | clevermous | 397 | invoke PciRead16, dword [.bus], dword [.devfn], 4 |
3520 | clevermous | 398 | push eax |
399 | ; 4b. Disable IO access. |
||
400 | and al, not 1 |
||
4418 | clevermous | 401 | invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax |
3520 | clevermous | 402 | ; 4c. Read&save IO base address. |
4418 | clevermous | 403 | invoke PciRead16, dword [.bus], dword [.devfn], 20h |
3520 | clevermous | 404 | and al, not 3 |
405 | xchg eax, edi |
||
406 | ; now edi = IO base |
||
407 | ; 4d. Write 0xffff to IO base address. |
||
4418 | clevermous | 408 | invoke PciWrite16, dword [.bus], dword [.devfn], 20h, -1 |
3520 | clevermous | 409 | ; 4e. Read IO base address. |
4418 | clevermous | 410 | invoke PciRead16, dword [.bus], dword [.devfn], 20h |
3520 | clevermous | 411 | and al, not 3 |
412 | cwde |
||
413 | not eax |
||
414 | inc eax |
||
415 | xchg eax, esi |
||
416 | ; now esi = IO size |
||
417 | ; 4f. Restore IO base address. |
||
4418 | clevermous | 418 | invoke PciWrite16, dword [.bus], dword [.devfn], 20h, edi |
3520 | clevermous | 419 | ; 4g. Restore PCI command state and enable io & bus master access. |
420 | pop ecx |
||
421 | or ecx, 5 |
||
4418 | clevermous | 422 | invoke PciWrite16, dword [.bus], dword [.devfn], 4, ecx |
3520 | clevermous | 423 | ; 5. Reset the controller. |
424 | ; 5e. Host reset. |
||
425 | mov edx, edi |
||
426 | mov ax, 2 |
||
427 | out dx, ax |
||
428 | ; 5f. Wait up to 10ms. |
||
3598 | clevermous | 429 | movi ecx, 10 |
3520 | clevermous | 430 | @@: |
431 | push esi |
||
3598 | clevermous | 432 | movi esi, 1 |
4418 | clevermous | 433 | invoke Sleep |
3520 | clevermous | 434 | pop esi |
435 | in ax, dx |
||
436 | test al, 2 |
||
437 | loopnz @b |
||
438 | jz @f |
||
439 | dbgstr 'UHCI controller reset timeout' |
||
440 | jmp .fail |
||
441 | @@: |
||
442 | if 0 |
||
443 | ; emergency variant for tests - always wait 10 ms |
||
444 | ; wait 10 ms |
||
445 | push esi |
||
3598 | clevermous | 446 | movi esi, 10 |
4418 | clevermous | 447 | invoke Sleep |
3520 | clevermous | 448 | pop esi |
449 | ; clear reset signal |
||
450 | xor eax, eax |
||
451 | out dx, ax |
||
452 | end if |
||
453 | .resetok: |
||
454 | ; 6. Get number of ports & disable all ports. |
||
455 | add esi, edi |
||
456 | lea edx, [edi+UhciPort1StatusReg] |
||
457 | .scanports: |
||
458 | cmp edx, esi |
||
459 | jae .doneports |
||
460 | in ax, dx |
||
461 | cmp ax, 0xFFFF |
||
462 | jz .doneports |
||
463 | test al, al |
||
464 | jns .doneports |
||
465 | xor eax, eax |
||
466 | out dx, ax |
||
467 | inc edx |
||
468 | inc edx |
||
469 | jmp .scanports |
||
470 | .doneports: |
||
471 | lea esi, [edx-UhciPort1StatusReg] |
||
472 | sub esi, edi |
||
473 | shr esi, 1 ; esi = number of ports |
||
474 | jnz @f |
||
475 | dbgstr 'error: no ports on UHCI controller' |
||
476 | jmp .fail |
||
477 | @@: |
||
478 | ; 7. Setup the rest of uhci_controller. |
||
479 | xchg esi, [esp] ; restore the pointer to uhci_controller from the step 1 |
||
480 | add esi, sizeof.uhci_controller |
||
481 | pop [esi+usb_controller.NumPorts] |
||
482 | DEBUGF 1,'K : UHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] |
||
483 | mov [esi+uhci_controller.IOBase-sizeof.uhci_controller], edi |
||
4418 | clevermous | 484 | invoke GetTimerTicks |
3520 | clevermous | 485 | mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax |
4418 | clevermous | 486 | ; 8. Find the EHCI companion. |
487 | ; If there is one, check whether all ports are covered by that companion. |
||
488 | ; Note: this assumes that EHCI is initialized before USB1 companions. |
||
489 | mov ebx, dword [.devfn] |
||
490 | invoke usbhc_api.usb_find_ehci_companion |
||
491 | mov [esi+uhci_controller.EhciCompanion-sizeof.uhci_controller], eax |
||
492 | ; 9. Hook interrupt. |
||
493 | invoke PciRead8, dword [.bus], dword [.devfn], 3Ch |
||
3520 | clevermous | 494 | ; al = IRQ |
495 | ; DEBUGF 1,'K : UHCI %x: io=%x, irq=%x\n',esi,edi,al |
||
496 | movzx eax, al |
||
4418 | clevermous | 497 | invoke AttachIntHandler, eax, uhci_irq, esi |
498 | ; 10. Setup controller registers. |
||
3520 | clevermous | 499 | xor eax, eax |
500 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
4418 | clevermous | 501 | ; 10a. UhciStatusReg := 3Fh: clear all status bits |
3520 | clevermous | 502 | ; (for this register 1 clears the corresponding bit, 0 does not change it). |
503 | inc edx |
||
504 | inc edx ; UhciStatusReg == 2 |
||
505 | mov al, 3Fh |
||
506 | out dx, ax |
||
4418 | clevermous | 507 | ; 10b. UhciInterruptReg := 0Dh. |
3520 | clevermous | 508 | inc edx |
509 | inc edx ; UhciInterruptReg == 4 |
||
510 | mov al, 0Dh |
||
511 | out dx, ax |
||
4418 | clevermous | 512 | ; 10c. UhciFrameNumberReg := 0. |
3520 | clevermous | 513 | inc edx |
514 | inc edx ; UhciFrameNumberReg == 6 |
||
515 | mov al, 0 |
||
516 | out dx, ax |
||
4418 | clevermous | 517 | ; 10d. UhciBaseAddressReg := physical address of uhci_controller. |
3520 | clevermous | 518 | inc edx |
519 | inc edx ; UhciBaseAddressReg == 8 |
||
520 | lea eax, [esi-sizeof.uhci_controller] |
||
4418 | clevermous | 521 | invoke GetPhysAddr |
3520 | clevermous | 522 | out dx, eax |
4418 | clevermous | 523 | ; 10e. UhciCommandReg := Run + Configured + (MaxPacket is 64 bytes) |
3520 | clevermous | 524 | sub edx, UhciBaseAddressReg ; UhciCommandReg == 0 |
525 | mov ax, 0C1h ; Run, Configured, MaxPacket = 64b |
||
526 | out dx, ax |
||
4418 | clevermous | 527 | ; 11. Do initial scan of existing devices. |
3520 | clevermous | 528 | call uhci_poll_roothub |
4418 | clevermous | 529 | ; 12. Return pointer to usb_controller. |
3520 | clevermous | 530 | xchg eax, esi |
531 | ret |
||
532 | .fail: |
||
533 | ; On error, pop the pointer saved at step 1 and return zero. |
||
4418 | clevermous | 534 | ; Note that the main code branch restores the stack at step 8 and never fails |
535 | ; after step 8. |
||
3520 | clevermous | 536 | pop ecx |
537 | xor eax, eax |
||
538 | ret |
||
539 | endp |
||
540 | |||
541 | ; Controller-specific pre-initialization function: take ownership from BIOS. |
||
542 | ; UHCI has no mechanism to ask the owner politely to release ownership, |
||
543 | ; so do it in inpolite way, preventing controller from any SMI activity. |
||
544 | proc uhci_kickoff_bios |
||
545 | ; 1. Get the I/O address. |
||
4418 | clevermous | 546 | invoke PciRead16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 20h |
3520 | clevermous | 547 | and eax, 0xFFFC |
548 | xchg eax, edx |
||
549 | ; 2. Stop the controller and disable all interrupts. |
||
550 | in ax, dx |
||
551 | and al, not 1 |
||
552 | out dx, ax |
||
553 | add edx, UhciInterruptReg |
||
554 | xor eax, eax |
||
555 | out dx, ax |
||
556 | ; 3. Disable all bits for SMI routing, clear SMI routing status, |
||
557 | ; enable master interrupt bit. |
||
4418 | clevermous | 558 | invoke PciWrite16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 0xC0, 0AF00h |
3520 | clevermous | 559 | ret |
560 | endp |
||
561 | |||
562 | ; Helper procedure for step 3 of uhci_init. |
||
563 | ; Initializes the static head of one list. |
||
564 | ; eax = physical address of the "next" list, esi = pointer to the "next" list, |
||
565 | ; edi = pointer to head to initialize. |
||
566 | ; Advances edi to the next head, keeps eax/esi. |
||
567 | proc uhci_init_static_endpoint |
||
568 | mov [edi+uhci_static_ep.NextQH], eax |
||
569 | mov byte [edi+uhci_static_ep.HeadTD], 1 |
||
570 | mov [edi+uhci_static_ep.NextList], esi |
||
571 | add edi, uhci_static_ep.SoftwarePart |
||
4418 | clevermous | 572 | invoke usbhc_api.usb_init_static_endpoint |
3520 | clevermous | 573 | add edi, sizeof.uhci_static_ep - uhci_static_ep.SoftwarePart |
574 | ret |
||
575 | endp |
||
576 | |||
577 | ; Helper procedure for step 3 of uhci_init, see comments there. |
||
578 | ; Initializes one half of group of static heads. |
||
579 | ; edx = size of the next group = half of size of the group, |
||
580 | ; edi = pointer to the group, eax = physical address of the next group, |
||
581 | ; esi = pointer to the next group. |
||
582 | ; Advances eax, esi, edi to next group, keeps edx. |
||
583 | proc uhci_init_static_ep_group |
||
584 | push edx |
||
585 | @@: |
||
586 | call uhci_init_static_endpoint |
||
587 | add eax, sizeof.uhci_static_ep |
||
588 | add esi, sizeof.uhci_static_ep |
||
589 | dec edx |
||
590 | jnz @b |
||
591 | pop edx |
||
592 | ret |
||
593 | endp |
||
594 | |||
595 | ; IRQ handler for UHCI controllers. |
||
596 | uhci_irq.noint: |
||
597 | ; Not our interrupt: restore esi and return zero. |
||
598 | pop esi |
||
599 | xor eax, eax |
||
600 | ret |
||
601 | proc uhci_irq |
||
602 | push esi ; save used register to be cdecl |
||
603 | virtual at esp |
||
604 | dd ? ; saved esi |
||
605 | dd ? ; return address |
||
606 | .controller dd ? |
||
607 | end virtual |
||
608 | mov esi, [.controller] |
||
609 | ; 1. Read UhciStatusReg. |
||
610 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
611 | inc edx |
||
612 | inc edx ; UhciStatusReg == 2 |
||
613 | in ax, dx |
||
614 | ; 2. Test whether it is our interrupt; if so, at least one status bit is set. |
||
615 | test al, 0x1F |
||
616 | jz .noint |
||
617 | ; 3. Clear all status bits. |
||
618 | out dx, ax |
||
619 | ; 4. Sanity check. |
||
620 | test al, 0x3C |
||
621 | jz @f |
||
622 | DEBUGF 1,'K : something terrible happened with UHCI (%x)\n',al |
||
623 | @@: |
||
624 | ; 5. We can't do too much from an interrupt handler, e.g. we can't take |
||
625 | ; any mutex locks since our code could be called when another code holds the |
||
626 | ; lock and has no chance to release it. Thus, only inform the processing thread |
||
627 | ; that it should scan the queue and wake it if needed. |
||
628 | lock or byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], al |
||
629 | push ebx |
||
630 | xor ebx, ebx |
||
631 | inc ebx |
||
4418 | clevermous | 632 | invoke usbhc_api.usb_wakeup_if_needed |
3520 | clevermous | 633 | pop ebx |
634 | ; 6. This is our interrupt; return 1. |
||
635 | mov al, 1 |
||
636 | pop esi ; restore used register to be stdcall |
||
637 | ret |
||
638 | endp |
||
639 | |||
640 | ; This procedure is called in the USB thread from usb_thread_proc, |
||
641 | ; processes regular actions and those actions which can't be safely done |
||
642 | ; from interrupt handler. |
||
643 | ; Returns maximal time delta before the next call. |
||
644 | proc uhci_process_deferred |
||
645 | push ebx edi ; save used registers to be stdcall |
||
646 | ; 1. Initialize the return value. |
||
647 | push -1 |
||
648 | ; 2. Poll the root hub every UHCI_POLL_INTERVAL ticks. |
||
649 | ; Also force polling if some transaction has completed with errors; |
||
650 | ; the error can be caused by disconnect, try to detect it. |
||
651 | test byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], 2 |
||
652 | jnz .force_poll |
||
4418 | clevermous | 653 | invoke GetTimerTicks |
3520 | clevermous | 654 | sub eax, [esi+uhci_controller.LastPollTime-sizeof.uhci_controller] |
655 | sub eax, UHCI_POLL_INTERVAL |
||
656 | jl .nopoll |
||
657 | .force_poll: |
||
4418 | clevermous | 658 | invoke GetTimerTicks |
3520 | clevermous | 659 | mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax |
660 | call uhci_poll_roothub |
||
661 | mov eax, -UHCI_POLL_INTERVAL |
||
662 | .nopoll: |
||
663 | neg eax |
||
664 | cmp [esp], eax |
||
665 | jb @f |
||
666 | mov [esp], eax |
||
667 | @@: |
||
668 | ; 3. Process wait lists. |
||
669 | ; 3a. Test whether there is a wait request. |
||
670 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
||
671 | cmp eax, [esi+usb_controller.ReadyPipeHeadAsync] |
||
672 | jnz .check_removed |
||
673 | mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] |
||
674 | cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic] |
||
675 | jz @f |
||
676 | .check_removed: |
||
677 | ; 3b. Yep. Find frame and compare it with the saved one. |
||
678 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
679 | add edx, UhciFrameNumberReg |
||
680 | in ax, dx |
||
681 | cmp word [esi+usb_controller.StartWaitFrame], ax |
||
682 | jnz .removed |
||
683 | ; 3c. The same frame; wake up in 0.01 sec. |
||
684 | mov dword [esp], 1 |
||
685 | jmp @f |
||
686 | .removed: |
||
687 | ; 3d. The frame is changed, old contents is guaranteed to be forgotten. |
||
688 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
||
689 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
||
690 | mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] |
||
691 | mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax |
||
692 | @@: |
||
693 | ; 4. Process disconnect events. This should be done after step 2 |
||
694 | ; (which includes the first stage of disconnect processing). |
||
4418 | clevermous | 695 | invoke usbhc_api.usb_disconnect_stage2 |
3520 | clevermous | 696 | ; 5. Test whether USB_CONNECT_DELAY for a connected device is over. |
697 | ; Call uhci_new_port for all such devices. |
||
698 | xor ecx, ecx |
||
699 | cmp [esi+usb_controller.NewConnected], ecx |
||
700 | jz .skip_newconnected |
||
701 | .portloop: |
||
702 | bt [esi+usb_controller.NewConnected], ecx |
||
703 | jnc .noconnect |
||
4418 | clevermous | 704 | ; If this port is shared with the EHCI companion and we see the connect event, |
705 | ; then the device is USB1 dropped by EHCI, |
||
706 | ; so EHCI has already waited for debounce delay, we can proceed immediately. |
||
707 | cmp [esi+uhci_controller.EhciCompanion-sizeof.uhci_controller], 0 |
||
708 | jz .portloop.test_time |
||
709 | dbgstr 'port is shared with EHCI, skipping initial debounce' |
||
710 | jmp .connected |
||
711 | .portloop.test_time: |
||
712 | invoke GetTimerTicks |
||
3520 | clevermous | 713 | sub eax, [esi+usb_controller.ConnectedTime+ecx*4] |
714 | sub eax, USB_CONNECT_DELAY |
||
715 | jge .connected |
||
716 | neg eax |
||
717 | cmp [esp], eax |
||
718 | jb .nextport |
||
719 | mov [esp], eax |
||
720 | jmp .nextport |
||
721 | .connected: |
||
722 | btr [esi+usb_controller.NewConnected], ecx |
||
723 | call uhci_new_port |
||
724 | .noconnect: |
||
725 | .nextport: |
||
726 | inc ecx |
||
727 | cmp ecx, [esi+usb_controller.NumPorts] |
||
728 | jb .portloop |
||
729 | .skip_newconnected: |
||
730 | ; 6. Test for processed packets. |
||
731 | ; This should be done after step 4, so transfers which were failed due |
||
732 | ; to disconnect are marked with the exact reason, not just |
||
733 | ; 'device not responding'. |
||
734 | xor eax, eax |
||
735 | xchg byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], al |
||
736 | test al, 3 |
||
737 | jz .noioc |
||
738 | call uhci_process_updated_schedule |
||
739 | .noioc: |
||
740 | ; 7. Test whether reset signalling has been started. If so, |
||
741 | ; either should be stopped now (if time is over) or schedule wakeup (otherwise). |
||
742 | ; This should be done after step 6, because a completed SET_ADDRESS command |
||
743 | ; could result in reset of a new port. |
||
744 | .test_reset: |
||
745 | ; 7a. Test whether reset signalling is active. |
||
746 | cmp [esi+usb_controller.ResettingStatus], 1 |
||
747 | jnz .no_reset_in_progress |
||
748 | ; 7b. Yep. Test whether it should be stopped. |
||
4418 | clevermous | 749 | invoke GetTimerTicks |
3520 | clevermous | 750 | sub eax, [esi+usb_controller.ResetTime] |
751 | sub eax, USB_RESET_TIME |
||
752 | jge .reset_done |
||
753 | ; 7c. Not yet, but initiate wakeup in -eax ticks and exit this step. |
||
754 | neg eax |
||
755 | cmp [esp], eax |
||
756 | jb .skip_reset |
||
757 | mov [esp], eax |
||
758 | jmp .skip_reset |
||
759 | .reset_done: |
||
760 | ; 7d. Yep, call the worker function and proceed to 7e. |
||
761 | call uhci_port_reset_done |
||
762 | .no_reset_in_progress: |
||
763 | ; 7e. Test whether reset process is done, either successful or failed. |
||
764 | cmp [esi+usb_controller.ResettingStatus], 0 |
||
765 | jz .skip_reset |
||
766 | ; 7f. Yep. Test whether it should be stopped. |
||
4418 | clevermous | 767 | invoke GetTimerTicks |
3520 | clevermous | 768 | sub eax, [esi+usb_controller.ResetTime] |
769 | sub eax, USB_RESET_RECOVERY_TIME |
||
770 | jge .reset_recovery_done |
||
771 | ; 7g. Not yet, but initiate wakeup in -eax ticks and exit this step. |
||
772 | neg eax |
||
773 | cmp [esp], eax |
||
774 | jb .skip_reset |
||
775 | mov [esp], eax |
||
776 | jmp .skip_reset |
||
777 | .reset_recovery_done: |
||
778 | ; 7h. Yep, call the worker function. This could initiate another reset, |
||
779 | ; so return to the beginning of this step. |
||
780 | call uhci_port_init |
||
781 | jmp .test_reset |
||
782 | .skip_reset: |
||
783 | ; 8. Process wait-done notifications, test for new wait requests. |
||
784 | ; Note: that must be done after steps 4 and 6 which could create new requests. |
||
785 | ; 8a. Call the worker function. |
||
4418 | clevermous | 786 | invoke usbhc_api.usb_process_wait_lists |
3520 | clevermous | 787 | ; 8b. If no new requests, skip the rest of this step. |
788 | test eax, eax |
||
789 | jz @f |
||
790 | ; 8c. UHCI is not allowed to cache anything; we don't know what is |
||
791 | ; processed right now, but we can be sure that the controller will not |
||
792 | ; use any removed structure starting from the next frame. |
||
793 | ; Request removal of everything disconnected until now, |
||
794 | ; schedule wakeup in 0.01 sec. |
||
795 | mov eax, [esi+usb_controller.WaitPipeListAsync] |
||
796 | mov [esi+usb_controller.WaitPipeRequestAsync], eax |
||
797 | mov eax, [esi+usb_controller.WaitPipeListPeriodic] |
||
798 | mov [esi+usb_controller.WaitPipeRequestPeriodic], eax |
||
799 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
800 | add edx, UhciFrameNumberReg |
||
801 | in ax, dx |
||
802 | mov word [esi+usb_controller.StartWaitFrame], ax |
||
803 | mov dword [esp], 1 |
||
804 | @@: |
||
805 | ; 9. Return the value from the top of stack. |
||
806 | pop eax |
||
807 | pop edi ebx ; restore used registers to be stdcall. |
||
808 | ret |
||
809 | endp |
||
810 | |||
811 | ; This procedure is called in the USB thread from uhci_process_deferred |
||
812 | ; when UHCI IRQ handler has signalled that new IOC-packet was processed. |
||
813 | ; It scans all lists for completed packets and calls uhci_process_finalized_td |
||
814 | ; for those packets. |
||
815 | ; in: esi -> usb_controller |
||
816 | proc uhci_process_updated_schedule |
||
817 | ; Important note: we cannot hold the list lock during callbacks, |
||
818 | ; because callbacks sometimes open and/or close pipes and thus acquire/release |
||
819 | ; the corresponding lock itself. |
||
820 | ; Fortunately, pipes can be finally freed only by another step of |
||
821 | ; uhci_process_deferred, so all pipes existing at the start of this function |
||
822 | ; will be valid while this function is running. Some pipes can be removed |
||
823 | ; from the corresponding list, some pipes can be inserted; insert/remove |
||
824 | ; functions guarantee that traversing one list yields all pipes that were in |
||
825 | ; that list at the beginning of the traversing (possibly with some new pipes, |
||
826 | ; possibly without some new pipes, that doesn't matter). |
||
827 | ; 1. Process all Periodic lists. |
||
828 | lea edi, [esi+uhci_controller.IntEDs.SoftwarePart-sizeof.uhci_controller] |
||
829 | lea ebx, [esi+uhci_controller.IntEDs.SoftwarePart+63*sizeof.uhci_static_ep-sizeof.uhci_controller] |
||
830 | @@: |
||
831 | call uhci_process_updated_list |
||
832 | cmp edi, ebx |
||
833 | jnz @b |
||
834 | ; 2. Process the Control list. |
||
835 | call uhci_process_updated_list |
||
836 | ; 3. Process the Bulk list. |
||
837 | call uhci_process_updated_list |
||
838 | ; 4. Return. |
||
839 | ret |
||
840 | endp |
||
841 | |||
842 | ; This procedure is called from uhci_process_updated_schedule, |
||
843 | ; see comments there. |
||
844 | ; It processes one list, esi -> usb_controller, edi -> usb_static_ep, |
||
845 | ; and advances edi to the next head. |
||
846 | proc uhci_process_updated_list |
||
847 | push ebx ; save used register to be stdcall |
||
848 | ; 1. Perform the external loop over all pipes. |
||
849 | mov ebx, [edi+usb_static_ep.NextVirt] |
||
850 | .loop: |
||
851 | cmp ebx, edi |
||
852 | jz .done |
||
853 | ; store pointer to the next pipe in the stack |
||
854 | push [ebx+usb_static_ep.NextVirt] |
||
855 | ; 2. For every pipe, perform the internal loop over all descriptors. |
||
856 | ; All descriptors are organized in the queue; we process items from the start |
||
857 | ; of the queue until a) the last descriptor (not the part of the queue itself) |
||
858 | ; or b) an active (not yet processed by the hardware) descriptor is reached. |
||
859 | lea ecx, [ebx+usb_pipe.Lock] |
||
4418 | clevermous | 860 | invoke MutexLock |
3520 | clevermous | 861 | mov ebx, [ebx+usb_pipe.LastTD] |
862 | push ebx |
||
863 | mov ebx, [ebx+usb_gtd.NextVirt] |
||
864 | .tdloop: |
||
865 | ; 3. For every descriptor, test active flag and check for end-of-queue; |
||
866 | ; if either of conditions holds, exit from the internal loop. |
||
867 | cmp ebx, [esp] |
||
868 | jz .tddone |
||
3653 | clevermous | 869 | mov eax, [ebx+uhci_gtd.ControlStatus-sizeof.uhci_gtd] |
3520 | clevermous | 870 | test eax, 1 shl 23 ; active? |
871 | jnz .tddone |
||
872 | ; Release the queue lock while processing one descriptor: |
||
873 | ; callback function could (and often would) schedule another transfer. |
||
874 | push ecx |
||
4418 | clevermous | 875 | invoke MutexUnlock |
3520 | clevermous | 876 | call uhci_process_finalized_td |
877 | pop ecx |
||
4418 | clevermous | 878 | invoke MutexLock |
3520 | clevermous | 879 | jmp .tdloop |
880 | .tddone: |
||
4418 | clevermous | 881 | invoke MutexUnlock |
3520 | clevermous | 882 | pop ebx |
883 | ; End of internal loop, restore pointer to the next pipe |
||
884 | ; and continue the external loop. |
||
885 | pop ebx |
||
886 | jmp .loop |
||
887 | .done: |
||
888 | pop ebx ; restore used register to be stdcall |
||
889 | add edi, sizeof.uhci_static_ep |
||
890 | ret |
||
891 | endp |
||
892 | |||
893 | ; This procedure is called from uhci_process_updated_list, which is itself |
||
894 | ; called from uhci_process_updated_schedule, see comments there. |
||
895 | ; It processes one completed descriptor. |
||
896 | ; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd. |
||
897 | proc uhci_process_finalized_td |
||
898 | ; 1. Remove this descriptor from the list of descriptors for this pipe. |
||
4418 | clevermous | 899 | invoke usbhc_api.usb_unlink_td |
3520 | clevermous | 900 | ; DEBUGF 1,'K : finalized TD:\n' |
901 | ; DEBUGF 1,'K : %x %x %x %x\n',[ebx-20],[ebx-16],[ebx-12],[ebx-8] |
||
902 | ; DEBUGF 1,'K : %x %x %x %x\n',[ebx-4],[ebx],[ebx+4],[ebx+8] |
||
903 | ; 2. If this is IN transfer into special buffer, copy the data |
||
904 | ; to target location. |
||
3653 | clevermous | 905 | mov edx, [ebx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] |
3520 | clevermous | 906 | and edx, not 1 ; clear lsb (used for another goal) |
907 | jz .nocopy |
||
3653 | clevermous | 908 | cmp byte [ebx+uhci_gtd.Token-sizeof.uhci_gtd], USB_PID_IN |
3520 | clevermous | 909 | jnz .nocopy |
910 | ; Note: we assume that pointer to buffer is valid in the memory space of |
||
911 | ; the USB thread. This means that buffer must reside in kernel memory |
||
912 | ; (shared by all processes). |
||
913 | push esi edi |
||
914 | mov esi, [edx+uhci_original_buffer.UsedBuffer] |
||
915 | mov edi, [edx+uhci_original_buffer.OrigBuffer] |
||
3653 | clevermous | 916 | mov ecx, [ebx+uhci_gtd.ControlStatus-sizeof.uhci_gtd] |
3520 | clevermous | 917 | inc ecx |
918 | and ecx, 7FFh |
||
919 | mov edx, ecx |
||
920 | shr ecx, 2 |
||
921 | and edx, 3 |
||
922 | rep movsd |
||
923 | mov ecx, edx |
||
924 | rep movsb |
||
925 | pop edi esi |
||
926 | .nocopy: |
||
927 | ; 3. Calculate actual number of bytes transferred. |
||
928 | ; 3a. Read the state. |
||
3653 | clevermous | 929 | mov eax, [ebx+uhci_gtd.ControlStatus-sizeof.uhci_gtd] |
930 | mov ecx, [ebx+uhci_gtd.Token-sizeof.uhci_gtd] |
||
3520 | clevermous | 931 | ; 3b. Get number of bytes processed. |
932 | lea edx, [eax+1] |
||
933 | and edx, 7FFh |
||
934 | ; 3c. Subtract number of bytes in this packet. |
||
935 | add ecx, 1 shl 21 |
||
936 | shr ecx, 21 |
||
937 | sub edx, ecx |
||
938 | ; 3d. Add total length transferred so far. |
||
939 | add edx, [ebx+usb_gtd.Length] |
||
940 | ; Actions on error and on success are slightly different. |
||
941 | ; 4. Test for error. On error, proceed to step 5, otherwise go to step 6 |
||
942 | ; with ecx = 0 (no error). |
||
943 | ; USB transaction error is always considered as such. |
||
944 | ; If short packets are not allowed, UHCI controllers do not set an error bit, |
||
945 | ; but stop (clear Active bit and do not advance) the queue. |
||
946 | ; Short packet is considered as an error if the packet is actually short |
||
947 | ; (actual length is less than maximal one) and the code creating the packet |
||
948 | ; requested that behaviour (so bit 0 of OrigBufferInfo is set; this could be |
||
949 | ; because the caller disallowed short packets or because the packet is not |
||
950 | ; the last one in the corresponding transfer). |
||
951 | xor ecx, ecx |
||
952 | test eax, 1 shl 22 |
||
953 | jnz .error |
||
3653 | clevermous | 954 | test byte [ebx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 1 |
3520 | clevermous | 955 | jz .notify |
956 | cmp edx, [ebx+usb_gtd.Length] |
||
957 | jz .notify |
||
958 | .error: |
||
959 | ; 5. There was an error while processing this packet. |
||
960 | ; The hardware has stopped processing the queue. |
||
961 | DEBUGF 1,'K : TD failed:\n' |
||
3653 | clevermous | 962 | if sizeof.uhci_gtd <> 20 |
3520 | clevermous | 963 | .err modify offsets for debug output |
964 | end if |
||
965 | DEBUGF 1,'K : %x %x %x %x\n',[ebx-20],[ebx-16],[ebx-12],[ebx-8] |
||
966 | DEBUGF 1,'K : %x %x %x %x\n',[ebx-4],[ebx],[ebx+4],[ebx+8] |
||
967 | ; 5a. Save the status and length. |
||
968 | push edx |
||
969 | push eax |
||
970 | mov eax, [ebx+usb_gtd.Pipe] |
||
3653 | clevermous | 971 | DEBUGF 1,'K : pipe: %x %x\n',[eax+0-sizeof.uhci_pipe],[eax+4-sizeof.uhci_pipe] |
3520 | clevermous | 972 | ; 5b. Store the current TD as an error packet. |
973 | ; If an error packet is already stored for this pipe, |
||
974 | ; it is definitely not used already, so free the old packet. |
||
3653 | clevermous | 975 | mov eax, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe] |
3520 | clevermous | 976 | test eax, eax |
977 | jz @f |
||
978 | stdcall uhci_free_td, eax |
||
979 | @@: |
||
980 | mov eax, [ebx+usb_gtd.Pipe] |
||
3653 | clevermous | 981 | mov [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe], ebx |
3520 | clevermous | 982 | ; 5c. Traverse the list of descriptors looking for the final packet |
983 | ; for this transfer. |
||
984 | ; Free and unlink non-final descriptors, except the current one. |
||
985 | ; Final descriptor will be freed in step 7. |
||
4418 | clevermous | 986 | invoke usbhc_api.usb_is_final_packet |
3520 | clevermous | 987 | jnc .found_final |
988 | mov ebx, [ebx+usb_gtd.NextVirt] |
||
989 | .look_final: |
||
4418 | clevermous | 990 | invoke usbhc_api.usb_unlink_td |
991 | invoke usbhc_api.usb_is_final_packet |
||
3520 | clevermous | 992 | jnc .found_final |
993 | push [ebx+usb_gtd.NextVirt] |
||
994 | stdcall uhci_free_td, ebx |
||
995 | pop ebx |
||
996 | jmp .look_final |
||
997 | .found_final: |
||
998 | ; 5d. Restore the status saved in 5a and transform it to the error code. |
||
999 | pop eax ; error code |
||
1000 | shr eax, 16 |
||
1001 | ; Notes: |
||
1002 | ; * any USB transaction error results in Stalled bit; if it is not set, |
||
1003 | ; but we are here, it must be due to short packet; |
||
1004 | ; * babble is considered a fatal USB transaction error, |
||
1005 | ; other errors just lead to retrying the transaction; |
||
1006 | ; if babble is detected, return the corresponding error; |
||
1007 | ; * if several non-fatal errors have occured during transaction retries, |
||
1008 | ; all corresponding bits are set. In this case, return some error code, |
||
1009 | ; the order is quite arbitrary. |
||
3598 | clevermous | 1010 | movi ecx, USB_STATUS_UNDERRUN |
3520 | clevermous | 1011 | test al, 1 shl (22-16) ; not Stalled? |
1012 | jz .know_error |
||
1013 | mov cl, USB_STATUS_OVERRUN |
||
1014 | test al, 1 shl (20-16) ; Babble detected? |
||
1015 | jnz .know_error |
||
1016 | mov cl, USB_STATUS_BITSTUFF |
||
1017 | test al, 1 shl (17-16) ; Bitstuff error? |
||
1018 | jnz .know_error |
||
1019 | mov cl, USB_STATUS_NORESPONSE |
||
1020 | test al, 1 shl (18-16) ; CRC/TimeOut error? |
||
1021 | jnz .know_error |
||
1022 | mov cl, USB_STATUS_BUFOVERRUN |
||
1023 | test al, 1 shl (21-16) ; Data Buffer error? |
||
1024 | jnz .know_error |
||
1025 | mov cl, USB_STATUS_STALL |
||
1026 | .know_error: |
||
1027 | ; 5e. If error code is USB_STATUS_UNDERRUN |
||
1028 | ; and the last TD allows short packets, it is not an error. |
||
1029 | ; Note: all TDs except the last one in any transfer stage are marked |
||
1030 | ; as short-packet-is-error to stop controller from further processing |
||
1031 | ; of that stage; we need to restart processing from a TD following the last. |
||
1032 | ; After that, go to step 6 with ecx = 0 (no error). |
||
1033 | cmp ecx, USB_STATUS_UNDERRUN |
||
1034 | jnz @f |
||
3653 | clevermous | 1035 | test byte [ebx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 1 |
3520 | clevermous | 1036 | jnz @f |
1037 | ; The controller has stopped this queue on the error packet. |
||
1038 | ; Update uhci_pipe.HeadTD to point to the next packet in the queue. |
||
1039 | call uhci_fix_toggle |
||
1040 | xor ecx, ecx |
||
1041 | .control: |
||
3653 | clevermous | 1042 | mov eax, [ebx+uhci_gtd.NextTD-sizeof.uhci_gtd] |
3520 | clevermous | 1043 | and al, not 0xF |
1044 | mov edx, [ebx+usb_gtd.Pipe] |
||
3653 | clevermous | 1045 | mov [edx+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax |
3520 | clevermous | 1046 | pop edx ; length |
1047 | jmp .notify |
||
1048 | @@: |
||
1049 | ; 5f. Abort the entire transfer. |
||
1050 | ; There are two cases: either there is only one transfer stage |
||
1051 | ; (everything except control transfers), then ebx points to the last TD and |
||
1052 | ; all previous TD were unlinked and dismissed (if possible), |
||
1053 | ; or there are several stages (a control transfer) and ebx points to the last |
||
1054 | ; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, |
||
1055 | ; because Setup stage can not produce short packets); for Data stage, we need |
||
1056 | ; to unlink and free (if possible) one more TD and advance ebx to the next one. |
||
1057 | cmp [ebx+usb_gtd.Callback], 0 |
||
1058 | jnz .normal |
||
1059 | ; We cannot free ErrorTD yet, it could still be used by the hardware. |
||
1060 | push ecx |
||
1061 | mov eax, [ebx+usb_gtd.Pipe] |
||
1062 | push [ebx+usb_gtd.NextVirt] |
||
3653 | clevermous | 1063 | cmp ebx, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe] |
3520 | clevermous | 1064 | jz @f |
1065 | stdcall uhci_free_td, ebx |
||
1066 | @@: |
||
1067 | pop ebx |
||
4418 | clevermous | 1068 | invoke usbhc_api.usb_unlink_td |
3520 | clevermous | 1069 | pop ecx |
1070 | .normal: |
||
1071 | ; 5g. For bulk/interrupt transfers we have no choice but halt the queue, |
||
1072 | ; the driver should intercede (through some API which is not written yet). |
||
1073 | ; Control pipes normally recover at the next SETUP transaction (first stage |
||
1074 | ; of any control transfer), so we hope on the best and just advance the queue |
||
1075 | ; to the next transfer. (According to the standard, "A control pipe may also |
||
1076 | ; support functional stall as well, but this is not recommended."). |
||
1077 | mov edx, [ebx+usb_gtd.Pipe] |
||
1078 | cmp [edx+usb_pipe.Type], CONTROL_PIPE |
||
1079 | jz .control |
||
1080 | ; Bulk/interrupt transfer; halt the queue. |
||
3653 | clevermous | 1081 | mov eax, [ebx+uhci_gtd.NextTD-sizeof.uhci_gtd] |
3520 | clevermous | 1082 | and al, not 0xF |
1083 | inc eax ; set Halted bit |
||
3653 | clevermous | 1084 | mov [edx+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax |
3520 | clevermous | 1085 | pop edx ; restore length saved in step 5a |
1086 | .notify: |
||
1087 | ; 6. Either the descriptor in ebx was processed without errors, |
||
1088 | ; or all necessary error actions were taken and ebx points to the last |
||
1089 | ; related descriptor. |
||
4418 | clevermous | 1090 | invoke usbhc_api.usb_process_gtd |
3520 | clevermous | 1091 | ; 7. Free the current descriptor (if allowed) and return the next one. |
1092 | ; 7a. Save pointer to the next descriptor. |
||
1093 | push [ebx+usb_gtd.NextVirt] |
||
1094 | ; 7b. Free the descriptor, unless it is saved as ErrorTD. |
||
1095 | mov eax, [ebx+usb_gtd.Pipe] |
||
3653 | clevermous | 1096 | cmp [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe], ebx |
3520 | clevermous | 1097 | jz @f |
1098 | stdcall uhci_free_td, ebx |
||
1099 | @@: |
||
1100 | ; 7c. Restore pointer to the next descriptor and return. |
||
1101 | pop ebx |
||
1102 | ret |
||
1103 | endp |
||
1104 | |||
1105 | ; Helper procedure for restarting transfer queue. |
||
1106 | ; When transfers are queued, their toggle bit is filled assuming that |
||
1107 | ; everything will go without errors. On error, some packets needs to be |
||
1108 | ; skipped, so toggle bits may become incorrect. |
||
1109 | ; This procedure fixes toggle bits. |
||
1110 | ; in: ebx -> last packet to be skipped, ErrorTD -> last processed packet |
||
1111 | proc uhci_fix_toggle |
||
1112 | ; 1. Nothing to do for control pipes: in that case, |
||
1113 | ; toggle bits for different transfer stages are independent. |
||
1114 | mov ecx, [ebx+usb_gtd.Pipe] |
||
1115 | cmp [ecx+usb_pipe.Type], CONTROL_PIPE |
||
1116 | jz .nothing |
||
1117 | ; 2. The hardware expects next packet with toggle = (ErrorTD.toggle xor 1), |
||
1118 | ; the current value in next packet is (ebx.toggle xor 1). |
||
1119 | ; Nothing to do if ErrorTD.toggle == ebx.toggle. |
||
3653 | clevermous | 1120 | mov eax, [ecx+uhci_pipe.ErrorTD-sizeof.uhci_pipe] |
1121 | mov eax, [eax+uhci_gtd.Token-sizeof.uhci_gtd] |
||
1122 | xor eax, [ebx+uhci_gtd.Token-sizeof.uhci_gtd] |
||
3520 | clevermous | 1123 | test eax, 1 shl 19 |
1124 | jz .nothing |
||
1125 | ; 3. Lock the transfer queue. |
||
1126 | add ecx, usb_pipe.Lock |
||
4418 | clevermous | 1127 | invoke MutexLock |
3520 | clevermous | 1128 | ; 4. Flip the toggle bit in all packets from ebx.NextVirt to ecx.LastTD |
1129 | ; (inclusive). |
||
1130 | mov eax, [ebx+usb_gtd.NextVirt] |
||
1131 | .loop: |
||
3653 | clevermous | 1132 | xor byte [eax+uhci_gtd.Token-sizeof.uhci_gtd+2], 1 shl (19-16) |
3520 | clevermous | 1133 | cmp eax, [ecx+usb_pipe.LastTD-usb_pipe.Lock] |
1134 | mov eax, [eax+usb_gtd.NextVirt] |
||
1135 | jnz .loop |
||
1136 | ; 5. Flip the toggle bit in uhci_pipe structure. |
||
3653 | clevermous | 1137 | xor byte [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock+2], 1 shl (19-16) |
3520 | clevermous | 1138 | ; 6. Unlock the transfer queue. |
4418 | clevermous | 1139 | invoke MutexUnlock |
3520 | clevermous | 1140 | .nothing: |
1141 | ret |
||
1142 | endp |
||
1143 | |||
1144 | ; This procedure is called in the USB thread from uhci_process_deferred |
||
1145 | ; every UHCI_POLL_INTERVAL ticks. It polls the controller for |
||
1146 | ; connect/disconnect events. |
||
1147 | ; in: esi -> usb_controller |
||
1148 | proc uhci_poll_roothub |
||
1149 | push ebx ; save used register to be stdcall |
||
1150 | ; 1. Prepare for the loop for every port. |
||
1151 | xor ecx, ecx |
||
1152 | .portloop: |
||
1153 | ; 2. Some implementations of UHCI set ConnectStatusChange bit in a response to |
||
1154 | ; PortReset. Thus, we must ignore this change for port which is resetting. |
||
1155 | cmp cl, [esi+usb_controller.ResettingPort] |
||
1156 | jz .nextport |
||
1157 | ; 3. Read port status. |
||
1158 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
1159 | lea edx, [edx+ecx*2+UhciPort1StatusReg] |
||
1160 | in ax, dx |
||
1161 | ; 4. If no change bits are set, continue to the next port. |
||
1162 | test al, 0Ah |
||
1163 | jz .nextport |
||
1164 | ; 5. Clear change bits and read the status again. |
||
1165 | ; (It is possible, although quite unlikely, that some event occurs between |
||
1166 | ; the first read and the clearing, invalidating the old status. If an event |
||
1167 | ; occurs after the clearing, we will not miss it, looking in the next scan. |
||
1168 | out dx, ax |
||
1169 | mov ebx, eax |
||
1170 | in ax, dx |
||
1171 | ; 6. Process connect change notifications. |
||
1172 | ; Note: if connect status has changed, ignore enable status change; |
||
1173 | ; it is normal to disable a port at disconnect event. |
||
1174 | ; Some controllers set enable status change bit, some don't. |
||
1175 | test bl, 2 |
||
1176 | jz .noconnectchange |
||
4418 | clevermous | 1177 | DEBUGF 1,'K : UHCI %x connect status changed, %x/%x\n',esi,bx,ax |
3520 | clevermous | 1178 | ; yep. Regardless of the current status, note disconnect event; |
1179 | ; if there is something connected, store the connect time and note connect event. |
||
1180 | ; In any way, do not process |
||
1181 | bts [esi+usb_controller.NewDisconnected], ecx |
||
1182 | test al, 1 |
||
1183 | jz .disconnect |
||
4418 | clevermous | 1184 | invoke GetTimerTicks |
3520 | clevermous | 1185 | mov [esi+usb_controller.ConnectedTime+ecx*4], eax |
1186 | bts [esi+usb_controller.NewConnected], ecx |
||
1187 | jmp .nextport |
||
1188 | .disconnect: |
||
1189 | btr [esi+usb_controller.NewConnected], ecx |
||
1190 | jmp .nextport |
||
1191 | .noconnectchange: |
||
1192 | ; 7. Process enable change notifications. |
||
1193 | ; Note: that needs work. |
||
1194 | test bl, 8 |
||
1195 | jz .nextport |
||
1196 | test al, 4 |
||
1197 | jnz .nextport |
||
1198 | dbgstr 'Port disabled' |
||
1199 | .nextport: |
||
1200 | ; 8. Continue the loop for every port. |
||
1201 | inc ecx |
||
1202 | cmp ecx, [esi+usb_controller.NumPorts] |
||
1203 | jb .portloop |
||
1204 | pop ebx ; restore used register to be stdcall |
||
1205 | ret |
||
1206 | endp |
||
1207 | |||
1208 | ; This procedure is called from uhci_process_deferred when |
||
1209 | ; a new device was connected at least USB_CONNECT_DELAY ticks |
||
1210 | ; and therefore is ready to be configured. |
||
1211 | ; in: esi -> usb_controller, ecx = port (zero-based) |
||
1212 | proc uhci_new_port |
||
1213 | ; test whether we are configuring another port |
||
1214 | ; if so, postpone configuring and return |
||
1215 | bts [esi+usb_controller.PendingPorts], ecx |
||
1216 | cmp [esi+usb_controller.ResettingPort], -1 |
||
1217 | jnz .nothing |
||
1218 | btr [esi+usb_controller.PendingPorts], ecx |
||
1219 | ; fall through to uhci_new_port.reset |
||
1220 | |||
1221 | ; This function is called from uhci_new_port and uhci_test_pending_port. |
||
1222 | ; It starts reset signalling for the port. Note that in USB first stages |
||
1223 | ; of configuration can not be done for several ports in parallel. |
||
1224 | .reset: |
||
1225 | ; 1. Store information about resetting hub (roothub) and port. |
||
1226 | and [esi+usb_controller.ResettingHub], 0 |
||
1227 | mov [esi+usb_controller.ResettingPort], cl |
||
1228 | ; 2. Initiate reset signalling. |
||
1229 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
1230 | lea edx, [edx+ecx*2+UhciPort1StatusReg] |
||
1231 | in ax, dx |
||
1232 | or ah, 2 |
||
1233 | out dx, ax |
||
1234 | ; 3. Store the current time and set status to 1 = reset signalling active. |
||
4418 | clevermous | 1235 | invoke GetTimerTicks |
3520 | clevermous | 1236 | mov [esi+usb_controller.ResetTime], eax |
1237 | mov [esi+usb_controller.ResettingStatus], 1 |
||
1238 | .nothing: |
||
1239 | ret |
||
1240 | endp |
||
1241 | |||
1242 | ; This procedure is called from uhci_process_deferred when |
||
1243 | ; reset signalling for a port needs to be finished. |
||
1244 | proc uhci_port_reset_done |
||
1245 | ; 1. Stop reset signalling. |
||
1246 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
1247 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
1248 | lea edx, [edx+ecx*2+UhciPort1StatusReg] |
||
1249 | in ax, dx |
||
4418 | clevermous | 1250 | DEBUGF 1,'K : UHCI %x status %x/',esi,ax |
3520 | clevermous | 1251 | and ah, not 2 |
1252 | out dx, ax |
||
1253 | ; 2. Status bits in UHCI are invalid during reset signalling. |
||
1254 | ; Wait a millisecond while status bits become valid again. |
||
1255 | push esi |
||
3598 | clevermous | 1256 | movi esi, 1 |
4418 | clevermous | 1257 | invoke Sleep |
3520 | clevermous | 1258 | pop esi |
1259 | ; 3. ConnectStatus bit is zero during reset and becomes 1 during step 2; |
||
1260 | ; some controllers interpret this as a (fake) connect event. |
||
1261 | ; Enable port and clear status change notification. |
||
1262 | in ax, dx |
||
1263 | DEBUGF 1,'%x\n',ax |
||
1264 | or al, 6 ; enable port, clear status change |
||
1265 | out dx, ax |
||
1266 | ; 4. Store the current time and set status to 2 = reset recovery active. |
||
4418 | clevermous | 1267 | invoke GetTimerTicks |
1268 | DEBUGF 1,'K : reset done\n' |
||
3520 | clevermous | 1269 | mov [esi+usb_controller.ResetTime], eax |
1270 | mov [esi+usb_controller.ResettingStatus], 2 |
||
1271 | ret |
||
1272 | endp |
||
1273 | |||
1274 | ; This procedure is called from uhci_process_deferred when |
||
1275 | ; a new device has been reset, recovered after reset and |
||
1276 | ; needs to be configured. |
||
1277 | ; in: esi -> usb_controller |
||
1278 | proc uhci_port_init |
||
1279 | ; 1. Read port status. |
||
1280 | mov [esi+usb_controller.ResettingStatus], 0 |
||
1281 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
1282 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
1283 | lea edx, [edx+ecx*2+UhciPort1StatusReg] |
||
1284 | in ax, dx |
||
4418 | clevermous | 1285 | DEBUGF 1,'K : UHCI %x status %x\n',esi,ax |
3520 | clevermous | 1286 | ; 2. If the device has been disconnected, stop the initialization. |
1287 | test al, 1 |
||
1288 | jnz @f |
||
1289 | dbgstr 'USB port disabled after reset' |
||
4418 | clevermous | 1290 | jmp [usbhc_api.usb_test_pending_port] |
3520 | clevermous | 1291 | @@: |
1292 | ; 3. Copy LowSpeed bit to bit 0 of eax and call the worker procedure |
||
1293 | ; to notify the protocol layer about new UHCI device. |
||
1294 | push edx |
||
1295 | mov al, ah |
||
1296 | call uhci_new_device |
||
1297 | pop edx |
||
1298 | test eax, eax |
||
1299 | jnz .nothing |
||
1300 | ; 4. If something at the protocol layer has failed |
||
1301 | ; (no memory, no bus address), disable the port and stop the initialization. |
||
1302 | .disable_exit: |
||
1303 | in ax, dx |
||
1304 | and al, not 4 |
||
1305 | out dx, ax ; disable the port |
||
4418 | clevermous | 1306 | jmp [usbhc_api.usb_test_pending_port] |
3520 | clevermous | 1307 | .nothing: |
1308 | ret |
||
1309 | endp |
||
1310 | |||
1311 | ; This procedure is called from uhci_port_init and from hub support code |
||
1312 | ; when a new device is connected and has been reset. |
||
1313 | ; It calls usb_new_device at the protocol layer with correct parameters. |
||
1314 | ; in: esi -> usb_controller, eax = speed; |
||
1315 | ; UHCI is USB1 device, so only low bit of eax (LowSpeed) is used. |
||
1316 | proc uhci_new_device |
||
1317 | ; 1. Clear all bits of speed except bit 0. |
||
1318 | and eax, 1 |
||
1319 | ; 2. Store the speed for the protocol layer. |
||
1320 | mov [esi+usb_controller.ResettingSpeed], al |
||
1321 | ; 3. Create pseudo-pipe in the stack. |
||
1322 | ; See uhci_init_pipe: only .Controller and .Token fields are used. |
||
1323 | push esi ; fill .Controller field |
||
1324 | mov ecx, esp |
||
1325 | shl eax, 20 ; bit 20 = LowSpeedDevice |
||
1326 | push eax ; ignored (ErrorTD) |
||
1327 | push eax ; .Token field: DeviceAddress is zero, bit 20 = LowSpeedDevice |
||
1328 | ; 4. Notify the protocol layer. |
||
4418 | clevermous | 1329 | invoke usbhc_api.usb_new_device |
3520 | clevermous | 1330 | ; 5. Cleanup the stack after step 3 and return. |
1331 | add esp, 12 |
||
1332 | ret |
||
1333 | endp |
||
1334 | |||
1335 | ; This procedure is called from usb_set_address_callback |
||
1336 | ; and stores USB device address in the uhci_pipe structure. |
||
1337 | ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address |
||
1338 | proc uhci_set_device_address |
||
3653 | clevermous | 1339 | mov byte [ebx+uhci_pipe.Token+1-sizeof.uhci_pipe], cl |
4418 | clevermous | 1340 | jmp [usbhc_api.usb_subscription_done] |
3520 | clevermous | 1341 | endp |
1342 | |||
1343 | ; This procedure returns USB device address from the uhci_pipe structure. |
||
1344 | ; in: esi -> usb_controller, ebx -> usb_pipe |
||
1345 | ; out: eax = endpoint address |
||
1346 | proc uhci_get_device_address |
||
3653 | clevermous | 1347 | mov al, byte [ebx+uhci_pipe.Token+1-sizeof.uhci_pipe] |
3520 | clevermous | 1348 | and eax, 7Fh |
1349 | ret |
||
1350 | endp |
||
1351 | |||
1352 | ; This procedure is called from usb_set_address_callback |
||
1353 | ; if the device does not accept SET_ADDRESS command and needs |
||
1354 | ; to be disabled at the port level. |
||
1355 | ; in: esi -> usb_controller, ecx = port (zero-based) |
||
1356 | proc uhci_port_disable |
||
1357 | mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] |
||
1358 | lea edx, [edx+UhciPort1StatusReg+ecx*2] |
||
1359 | in ax, dx |
||
1360 | and al, not 4 |
||
1361 | out dx, ax |
||
1362 | ret |
||
1363 | endp |
||
1364 | |||
1365 | ; This procedure is called from usb_get_descr8_callback when |
||
1366 | ; the packet size for zero endpoint becomes known and |
||
1367 | ; stores the packet size in uhci_pipe structure. |
||
1368 | ; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size |
||
1369 | proc uhci_set_endpoint_packet_size |
||
1370 | dec ecx |
||
1371 | shl ecx, 21 |
||
3653 | clevermous | 1372 | and [ebx+uhci_pipe.Token-sizeof.uhci_pipe], (1 shl 21) - 1 |
1373 | or [ebx+uhci_pipe.Token-sizeof.uhci_pipe], ecx |
||
3520 | clevermous | 1374 | ; uhci_pipe.Token field is purely for software bookkeeping and does not affect |
1375 | ; the hardware; thus, we can continue initialization immediately. |
||
4418 | clevermous | 1376 | jmp [usbhc_api.usb_subscription_done] |
3520 | clevermous | 1377 | endp |
1378 | |||
1379 | ; This procedure is called from API usb_open_pipe and processes |
||
1380 | ; the controller-specific part of this API. See docs. |
||
1381 | ; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe, |
||
1382 | ; esi -> usb_controller, eax -> usb_gtd for the first TD, |
||
1383 | ; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type |
||
1384 | proc uhci_init_pipe |
||
1385 | ; inherit some variables from the parent usb_open_pipe |
||
3816 | clevermous | 1386 | virtual at ebp-12 |
1387 | .speed db ? |
||
1388 | rb 3 |
||
1389 | .bandwidth dd ? |
||
1390 | .target dd ? |
||
1391 | rd 2 |
||
3520 | clevermous | 1392 | .config_pipe dd ? |
1393 | .endpoint dd ? |
||
1394 | .maxpacket dd ? |
||
1395 | .type dd ? |
||
1396 | .interval dd ? |
||
1397 | end virtual |
||
1398 | ; 1. Initialize ErrorTD to zero. |
||
3653 | clevermous | 1399 | and [edi+uhci_pipe.ErrorTD-sizeof.uhci_pipe], 0 |
3520 | clevermous | 1400 | ; 2. Initialize HeadTD to the physical address of the first TD. |
3700 | clevermous | 1401 | push eax ; store pointer to the first TD for step 4 |
3653 | clevermous | 1402 | sub eax, sizeof.uhci_gtd |
4418 | clevermous | 1403 | invoke GetPhysAddr |
3653 | clevermous | 1404 | mov [edi+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax |
3520 | clevermous | 1405 | ; 3. Initialize Token field: |
1406 | ; take DeviceAddress and LowSpeedDevice from the parent pipe, |
||
1407 | ; take Endpoint and MaximumLength fields from API arguments, |
||
1408 | ; set PID depending on pipe type and provided pipe direction, |
||
1409 | ; set DataToggle to zero. |
||
3653 | clevermous | 1410 | mov eax, [ecx+uhci_pipe.Token-sizeof.uhci_pipe] |
3520 | clevermous | 1411 | and eax, 0x107F00 ; keep DeviceAddress and LowSpeedDevice |
1412 | mov edx, [.endpoint] |
||
1413 | and edx, 15 |
||
1414 | shl edx, 15 |
||
1415 | or eax, edx |
||
1416 | mov edx, [.maxpacket] |
||
1417 | dec edx |
||
1418 | shl edx, 21 |
||
1419 | or eax, edx |
||
1420 | mov al, USB_PID_SETUP |
||
1421 | cmp [.type], CONTROL_PIPE |
||
1422 | jz @f |
||
1423 | mov al, USB_PID_OUT |
||
1424 | test byte [.endpoint], 80h |
||
1425 | jz @f |
||
1426 | mov al, USB_PID_IN |
||
1427 | @@: |
||
3653 | clevermous | 1428 | mov [edi+uhci_pipe.Token-sizeof.uhci_pipe], eax |
3816 | clevermous | 1429 | bt eax, 20 |
1430 | setc [.speed] |
||
3520 | clevermous | 1431 | ; 4. Initialize the first TD: |
1432 | ; copy Token from uhci_pipe.Token zeroing reserved bit 20, |
||
1433 | ; set ControlStatus for future transfers, bit make it inactive, |
||
3700 | clevermous | 1434 | ; set bit 0 in NextTD = "no next TD", |
1435 | ; zero OrigBufferInfo. |
||
3520 | clevermous | 1436 | pop edx ; restore pointer saved in step 2 |
3653 | clevermous | 1437 | mov [edx+uhci_gtd.Token-sizeof.uhci_gtd], eax |
1438 | and byte [edx+uhci_gtd.Token+2-sizeof.uhci_gtd], not (1 shl (20-16)) |
||
3520 | clevermous | 1439 | and eax, 1 shl 20 |
1440 | shl eax, 6 |
||
1441 | or eax, UHCI_INVALID_LENGTH + (3 shl 27) |
||
1442 | ; not processed, inactive, allow 3 errors |
||
3700 | clevermous | 1443 | and [edx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 0 |
3653 | clevermous | 1444 | mov [edx+uhci_gtd.ControlStatus-sizeof.uhci_gtd], eax |
1445 | mov [edx+uhci_gtd.NextTD-sizeof.uhci_gtd], 1 |
||
3520 | clevermous | 1446 | ; 5. Select the corresponding list and insert to the list. |
1447 | ; 5a. Use Control list for control pipes, Bulk list for bulk pipes. |
||
1448 | lea edx, [esi+uhci_controller.ControlED.SoftwarePart-sizeof.uhci_controller] |
||
1449 | cmp [.type], BULK_PIPE |
||
1450 | jb .insert ; control pipe |
||
1451 | lea edx, [esi+uhci_controller.BulkED.SoftwarePart-sizeof.uhci_controller] |
||
1452 | jz .insert ; bulk pipe |
||
1453 | .interrupt_pipe: |
||
1454 | ; 5b. For interrupt pipes, let the scheduler select the appropriate list |
||
1455 | ; based on the current bandwidth distribution and the requested bandwidth. |
||
1456 | ; This could fail if the requested bandwidth is not available; |
||
1457 | ; if so, return an error. |
||
1458 | lea edx, [esi + uhci_controller.IntEDs - sizeof.uhci_controller] |
||
1459 | lea eax, [esi + uhci_controller.IntEDs + 32*sizeof.uhci_static_ep - sizeof.uhci_controller] |
||
3598 | clevermous | 1460 | movi ecx, 64 |
3520 | clevermous | 1461 | call usb1_select_interrupt_list |
1462 | test edx, edx |
||
1463 | jz .return0 |
||
1464 | .insert: |
||
4547 | clevermous | 1465 | mov [edi+usb_pipe.BaseList], edx |
3520 | clevermous | 1466 | ; Insert to the head of the corresponding list. |
1467 | ; Note: inserting to the head guarantees that the list traverse in |
||
1468 | ; uhci_process_updated_schedule, once started, will not interact with new pipes. |
||
1469 | ; However, we still need to ensure that links in the new pipe (edi.NextVirt) |
||
1470 | ; are initialized before links to the new pipe (edx.NextVirt). |
||
1471 | ; 5c. Insert in the list of virtual addresses. |
||
1472 | mov ecx, [edx+usb_pipe.NextVirt] |
||
1473 | mov [edi+usb_pipe.NextVirt], ecx |
||
1474 | mov [edi+usb_pipe.PrevVirt], edx |
||
1475 | mov [ecx+usb_pipe.PrevVirt], edi |
||
1476 | mov [edx+usb_pipe.NextVirt], edi |
||
1477 | ; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, |
||
1478 | ; store the physical address of the new pipe to previous NextQH. |
||
1479 | mov ecx, [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart] |
||
3653 | clevermous | 1480 | mov [edi+uhci_pipe.NextQH-sizeof.uhci_pipe], ecx |
1481 | lea eax, [edi-sizeof.uhci_pipe] |
||
4418 | clevermous | 1482 | invoke GetPhysAddr |
3520 | clevermous | 1483 | inc eax |
1484 | inc eax |
||
1485 | mov [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart], eax |
||
1486 | ; 6. Return with nonzero eax. |
||
1487 | ret |
||
1488 | .return0: |
||
1489 | xor eax, eax |
||
1490 | ret |
||
1491 | endp |
||
1492 | |||
1493 | ; This procedure is called when a pipe is closing (either due to API call |
||
1494 | ; or due to disconnect); it unlinks a pipe from the corresponding list. |
||
3653 | clevermous | 1495 | if uhci_static_ep.SoftwarePart <> sizeof.uhci_pipe |
1496 | .err uhci_unlink_pipe assumes that uhci_static_ep.SoftwarePart == sizeof.uhci_pipe |
||
3520 | clevermous | 1497 | end if |
1498 | proc uhci_unlink_pipe |
||
1499 | cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE |
||
1500 | jnz @f |
||
3653 | clevermous | 1501 | mov eax, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] |
3520 | clevermous | 1502 | cmp al, USB_PID_IN |
1503 | setz ch |
||
1504 | bt eax, 20 |
||
1505 | setc cl |
||
1506 | add eax, 1 shl 21 |
||
1507 | shr eax, 21 |
||
1508 | stdcall usb1_interrupt_list_unlink, eax, ecx |
||
1509 | @@: |
||
4547 | clevermous | 1510 | ret |
1511 | endp |
||
1512 | |||
1513 | ; This procedure temporarily removes the given pipe from hardware queue, |
||
1514 | ; keeping it in software lists. |
||
1515 | ; esi -> usb_controller, ebx -> usb_pipe |
||
1516 | proc uhci_disable_pipe |
||
1517 | mov eax, [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe] |
||
1518 | mov edx, [ebx+usb_pipe.PrevVirt] |
||
1519 | ; Note: edx could be either usb_pipe or usb_static_ep; |
||
3520 | clevermous | 1520 | ; fortunately, NextQH and SoftwarePart have same offsets in both. |
4547 | clevermous | 1521 | mov [edx+uhci_pipe.NextQH-sizeof.uhci_pipe], eax |
3520 | clevermous | 1522 | ret |
1523 | endp |
||
1524 | |||
4547 | clevermous | 1525 | ; This procedure reinserts the given pipe from hardware queue |
1526 | ; after ehci_disable_pipe, with clearing transfer queue. |
||
1527 | ; esi -> usb_controller, ebx -> usb_pipe |
||
1528 | ; edx -> current descriptor, eax -> new last descriptor |
||
1529 | proc uhci_enable_pipe |
||
1530 | ; 1. Copy DataToggle bit from edx to pipe. |
||
1531 | mov ecx, [edx+uhci_gtd.Token-sizeof.uhci_gtd] |
||
1532 | xor ecx, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] |
||
1533 | and ecx, 1 shl 19 |
||
1534 | xor [ebx+uhci_pipe.Token-sizeof.uhci_pipe], ecx |
||
1535 | ; 2. Store new last descriptor as the current HeadTD. |
||
1536 | sub eax, sizeof.uhci_gtd |
||
1537 | invoke GetPhysAddr |
||
1538 | mov [ebx+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax |
||
1539 | ; 3. Reinsert the pipe to hardware queue. |
||
1540 | lea eax, [ebx-sizeof.uhci_pipe] |
||
1541 | invoke GetPhysAddr |
||
1542 | inc eax |
||
1543 | inc eax |
||
1544 | mov edx, [ebx+usb_pipe.PrevVirt] |
||
1545 | mov ecx, [edx+uhci_pipe.NextQH-sizeof.uhci_pipe] |
||
1546 | mov [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe], ecx |
||
1547 | mov [edx+uhci_pipe.NextQH-sizeof.uhci_pipe], eax |
||
1548 | ret |
||
1549 | endp |
||
1550 | |||
3520 | clevermous | 1551 | ; This procedure is called from the several places in main USB code |
1552 | ; and allocates required packets for the given transfer stage. |
||
1553 | ; ebx = pipe, other parameters are passed through the stack |
||
1554 | proc uhci_alloc_transfer stdcall uses edi, buffer:dword, size:dword, flags:dword, td:dword, direction:dword |
||
1555 | locals |
||
1556 | token dd ? |
||
1557 | origTD dd ? |
||
1558 | packetSize dd ? ; must be the last variable, see usb_init_transfer |
||
1559 | endl |
||
1560 | ; 1. [td] will be the first packet in the transfer. |
||
1561 | ; Save it to allow unrolling if something will fail. |
||
1562 | mov eax, [td] |
||
1563 | mov [origTD], eax |
||
1564 | ; In UHCI one TD describes one packet, transfers should be split into parts |
||
1565 | ; with size <= endpoint max packet size. |
||
1566 | ; 2. Get the maximum packet size for endpoint from uhci_pipe.Token |
||
1567 | ; and generate Token field for TDs. |
||
3653 | clevermous | 1568 | mov edi, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] |
3520 | clevermous | 1569 | mov eax, edi |
1570 | shr edi, 21 |
||
1571 | inc edi |
||
1572 | ; zero packet size (it will be set for every packet individually), |
||
1573 | ; zero reserved bit 20, |
||
1574 | and eax, (1 shl 20) - 1 |
||
1575 | mov [packetSize], edi |
||
1576 | ; set the correct PID if it is different from the pipe-wide PID |
||
1577 | ; (Data and Status stages of control transfers), |
||
1578 | mov ecx, [direction] |
||
1579 | and ecx, 3 |
||
1580 | jz @f |
||
1581 | mov al, USB_PID_OUT |
||
1582 | dec ecx |
||
1583 | jz @f |
||
1584 | mov al, USB_PID_IN |
||
1585 | @@: |
||
1586 | ; set the toggle bit for control transfers, |
||
1587 | mov ecx, [direction] |
||
1588 | test cl, 1 shl 3 |
||
1589 | jz @f |
||
1590 | and ecx, 1 shl 2 |
||
1591 | and eax, not (1 shl 19) |
||
1592 | shl ecx, 19-2 |
||
1593 | or eax, ecx |
||
1594 | @@: |
||
1595 | ; store the resulting Token in the stack variable. |
||
1596 | mov [token], eax |
||
1597 | ; 3. While the remaining data cannot fit in one packet, |
||
1598 | ; allocate full packets (of maximal possible size). |
||
1599 | .fullpackets: |
||
1600 | cmp [size], edi |
||
1601 | jbe .lastpacket |
||
1602 | call uhci_alloc_packet |
||
1603 | test eax, eax |
||
1604 | jz .fail |
||
1605 | mov [td], eax |
||
1606 | add [buffer], edi |
||
1607 | sub [size], edi |
||
1608 | jmp .fullpackets |
||
1609 | .lastpacket: |
||
1610 | ; 4. The remaining data can fit in one packet; |
||
1611 | ; allocate the last packet with size = size of remaining data. |
||
1612 | mov eax, [size] |
||
1613 | mov [packetSize], eax |
||
1614 | call uhci_alloc_packet |
||
1615 | test eax, eax |
||
1616 | jz .fail |
||
1617 | ; 5. Clear 'short packets are not allowed' bit for the last packet, |
||
1618 | ; if the caller requested this. |
||
1619 | ; Note: even if the caller says that short transfers are ok, |
||
1620 | ; all packets except the last one are marked as 'must be complete': |
||
1621 | ; if one of them will be short, the software intervention is needed |
||
1622 | ; to skip remaining packets; uhci_process_finalized_td will handle this |
||
1623 | ; transparently to the caller. |
||
1624 | test [flags], 1 |
||
1625 | jz @f |
||
3653 | clevermous | 1626 | and byte [ecx+uhci_gtd.ControlStatus+3-sizeof.uhci_gtd], not (1 shl (29-24)) |
1627 | and byte [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], not 1 |
||
3520 | clevermous | 1628 | @@: |
1629 | ; 6. Update toggle bit in uhci_pipe structure from current value of [token]. |
||
1630 | mov edx, [token] |
||
3653 | clevermous | 1631 | xor edx, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] |
3520 | clevermous | 1632 | and edx, 1 shl 19 |
3653 | clevermous | 1633 | xor [ebx+uhci_pipe.Token-sizeof.uhci_pipe], edx |
3520 | clevermous | 1634 | .nothing: |
1635 | ret |
||
1636 | .fail: |
||
1637 | mov edi, uhci_hardware_func |
||
1638 | mov eax, [td] |
||
4418 | clevermous | 1639 | invoke usbhc_api.usb_undo_tds, [origTD] |
3520 | clevermous | 1640 | xor eax, eax |
1641 | jmp .nothing |
||
1642 | endp |
||
1643 | |||
1644 | ; Helper procedure for uhci_alloc_transfer. Allocates one packet. |
||
1645 | proc uhci_alloc_packet |
||
1646 | ; inherit some variables from the parent uhci_alloc_transfer |
||
1647 | virtual at ebp-12 |
||
1648 | .token dd ? |
||
1649 | .origTD dd ? |
||
1650 | .packetSize dd ? |
||
1651 | rd 2 |
||
1652 | .buffer dd ? |
||
1653 | .transferSize dd ? |
||
1654 | .Flags dd ? |
||
1655 | .td dd ? |
||
1656 | .direction dd ? |
||
1657 | end virtual |
||
1658 | ; 1. In UHCI all data for one packet must be on the same page. |
||
1659 | ; Thus, if the given buffer splits page boundary, we need a temporary buffer |
||
1660 | ; and code that transfers data between the given buffer and the temporary one. |
||
1661 | ; 1a. There is no buffer for zero-length packets. |
||
1662 | xor eax, eax |
||
1663 | cmp [.packetSize], eax |
||
1664 | jz .notempbuf |
||
1665 | ; 1b. A temporary buffer is not required if the first and the last bytes |
||
1666 | ; of the given buffer are the same except lower 12 bits. |
||
1667 | mov edx, [.buffer] |
||
1668 | add edx, [.packetSize] |
||
1669 | dec edx |
||
1670 | xor edx, [.buffer] |
||
1671 | test edx, -0x1000 |
||
1672 | jz .notempbuf |
||
1673 | ; 1c. We need a temporary buffer. Allocate [packetSize]*2 bytes, so that |
||
1674 | ; there must be [packetSize] bytes on one page, |
||
1675 | ; plus space for a header uhci_original_buffer. |
||
1676 | mov eax, [.packetSize] |
||
1677 | add eax, eax |
||
1678 | add eax, sizeof.uhci_original_buffer |
||
4418 | clevermous | 1679 | invoke Kmalloc |
3520 | clevermous | 1680 | ; 1d. If failed, return zero. |
1681 | test eax, eax |
||
1682 | jz .nothing |
||
1683 | ; 1e. Test whether [.packetSize] bytes starting from |
||
1684 | ; eax + sizeof.uhci_original_buffer are in the same page. |
||
1685 | ; If so, use eax + sizeof.uhci_original_buffer as a temporary buffer. |
||
1686 | ; Otherwise, use the beginning of the next page as a temporary buffer |
||
1687 | ; (since we have overallocated, sufficient space must remain). |
||
1688 | lea ecx, [eax+sizeof.uhci_original_buffer] |
||
1689 | mov edx, ecx |
||
1690 | add edx, [.packetSize] |
||
1691 | dec edx |
||
1692 | xor edx, ecx |
||
1693 | test edx, -0x1000 |
||
1694 | jz @f |
||
1695 | mov ecx, eax |
||
1696 | or ecx, 0xFFF |
||
1697 | inc ecx |
||
1698 | @@: |
||
1699 | mov [eax+uhci_original_buffer.UsedBuffer], ecx |
||
1700 | mov ecx, [.buffer] |
||
1701 | mov [eax+uhci_original_buffer.OrigBuffer], ecx |
||
1702 | ; 1f. For SETUP and OUT packets, copy data from the given buffer |
||
1703 | ; to the temporary buffer now. For IN packets, data go in other direction |
||
1704 | ; when the transaction completes. |
||
1705 | cmp byte [.token], USB_PID_IN |
||
1706 | jz .nocopy |
||
1707 | push esi edi |
||
1708 | mov esi, ecx |
||
1709 | mov edi, [eax+uhci_original_buffer.UsedBuffer] |
||
1710 | mov ecx, [.packetSize] |
||
1711 | mov edx, ecx |
||
1712 | shr ecx, 2 |
||
1713 | and edx, 3 |
||
1714 | rep movsd |
||
1715 | mov ecx, edx |
||
1716 | rep movsb |
||
1717 | pop edi esi |
||
1718 | .nocopy: |
||
1719 | .notempbuf: |
||
1720 | ; 2. Allocate the next TD. |
||
1721 | push eax |
||
4418 | clevermous | 1722 | call uhci_alloc_td |
3520 | clevermous | 1723 | pop edx |
1724 | ; If failed, free the temporary buffer (if it was allocated) and return zero. |
||
1725 | test eax, eax |
||
1726 | jz .fail |
||
1727 | ; 3. Initialize controller-independent parts of both TDs. |
||
1728 | push edx |
||
4418 | clevermous | 1729 | invoke usbhc_api.usb_init_transfer |
3520 | clevermous | 1730 | ; 4. Initialize the next TD: |
1731 | ; mark it as last one (this will be changed when further packets will be |
||
1732 | ; allocated), copy Token field from uhci_pipe.Token zeroing bit 20, |
||
1733 | ; generate ControlStatus field, mark as Active |
||
3699 | clevermous | 1734 | ; (for last descriptor, this will be changed by uhci_insert_transfer), |
1735 | ; zero OrigBufferInfo (otherwise uhci_free_td would try to free it). |
||
1736 | and [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 0 |
||
3653 | clevermous | 1737 | mov [eax+uhci_gtd.NextTD-sizeof.uhci_gtd], 1 ; no next TD |
1738 | mov edx, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] |
||
1739 | mov [eax+uhci_gtd.Token-sizeof.uhci_gtd], edx |
||
1740 | and byte [eax+uhci_gtd.Token+2-sizeof.uhci_gtd], not (1 shl (20-16)) |
||
3520 | clevermous | 1741 | and edx, 1 shl 20 |
1742 | shl edx, 6 |
||
1743 | or edx, UHCI_INVALID_LENGTH + (1 shl 23) + (3 shl 27) |
||
1744 | ; not processed, active, allow 3 errors |
||
3653 | clevermous | 1745 | mov [eax+uhci_gtd.ControlStatus-sizeof.uhci_gtd], edx |
3520 | clevermous | 1746 | ; 5. Initialize remaining fields of the current TD. |
1747 | ; 5a. Store pointer to the buffer allocated in step 1 (or zero). |
||
3653 | clevermous | 1748 | pop [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] |
3520 | clevermous | 1749 | ; 5b. Store physical address of the next TD. |
1750 | push eax |
||
3653 | clevermous | 1751 | sub eax, sizeof.uhci_gtd |
4418 | clevermous | 1752 | invoke GetPhysAddr |
3656 | clevermous | 1753 | ; for Control/Bulk pipes, use Depth traversal unless this is the first TD |
1754 | ; in the transfer stage; |
||
3520 | clevermous | 1755 | ; uhci_insert_transfer will set Depth traversal for the first TD and clear |
1756 | ; it in the last TD |
||
3656 | clevermous | 1757 | test [ebx+usb_pipe.Type], 1 |
1758 | jnz @f |
||
3520 | clevermous | 1759 | cmp ecx, [ebx+usb_pipe.LastTD] |
1760 | jz @f |
||
1761 | or eax, 4 |
||
1762 | @@: |
||
3653 | clevermous | 1763 | mov [ecx+uhci_gtd.NextTD-sizeof.uhci_gtd], eax |
3520 | clevermous | 1764 | ; 5c. Store physical address of the buffer: zero if no data present, |
1765 | ; the temporary buffer if it was allocated, the given buffer otherwise. |
||
1766 | xor eax, eax |
||
1767 | cmp [.packetSize], eax |
||
1768 | jz .hasphysbuf |
||
1769 | mov eax, [.buffer] |
||
3653 | clevermous | 1770 | mov edx, [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] |
3520 | clevermous | 1771 | test edx, edx |
1772 | jz @f |
||
1773 | mov eax, [edx+uhci_original_buffer.UsedBuffer] |
||
1774 | @@: |
||
4418 | clevermous | 1775 | invoke GetPhysAddr |
3520 | clevermous | 1776 | .hasphysbuf: |
3653 | clevermous | 1777 | mov [ecx+uhci_gtd.Buffer-sizeof.uhci_gtd], eax |
3520 | clevermous | 1778 | ; 5d. For IN transfers, disallow short packets. |
1779 | ; This will be overridden, if needed, by uhci_alloc_transfer. |
||
1780 | mov eax, [.token] |
||
1781 | mov edx, [.packetSize] |
||
1782 | dec edx |
||
1783 | cmp al, USB_PID_IN |
||
1784 | jnz @f |
||
3653 | clevermous | 1785 | or byte [ecx+uhci_gtd.ControlStatus+3-sizeof.uhci_gtd], 1 shl (29-24) ; disallow short packets |
1786 | or byte [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 1 |
||
3520 | clevermous | 1787 | @@: |
1788 | ; 5e. Get Token field: combine [.token] with [.packetSize]. |
||
1789 | shl edx, 21 |
||
1790 | or edx, eax |
||
3653 | clevermous | 1791 | mov [ecx+uhci_gtd.Token-sizeof.uhci_gtd], edx |
3520 | clevermous | 1792 | ; 6. Flip toggle bit in [.token]. |
1793 | xor eax, 1 shl 19 |
||
1794 | mov [.token], eax |
||
1795 | ; 7. Return pointer to the next TD. |
||
1796 | pop eax |
||
1797 | .nothing: |
||
1798 | ret |
||
1799 | .fail: |
||
1800 | xchg eax, edx |
||
4418 | clevermous | 1801 | invoke Kfree |
3520 | clevermous | 1802 | xor eax, eax |
1803 | ret |
||
1804 | endp |
||
1805 | |||
1806 | ; This procedure is called from the several places in main USB code |
||
1807 | ; and activates the transfer which was previously allocated by |
||
1808 | ; uhci_alloc_transfer. |
||
1809 | ; ecx -> last descriptor for the transfer, ebx -> usb_pipe |
||
1810 | proc uhci_insert_transfer |
||
1811 | ; DEBUGF 1,'K : uhci_insert_transfer: eax=%x, ecx=%x, [esp+4]=%x\n',eax,ecx,[esp+4] |
||
3653 | clevermous | 1812 | and byte [eax+uhci_gtd.ControlStatus+2-sizeof.uhci_gtd], not (1 shl (23-16)) ; clear Active bit |
1813 | or byte [ecx+uhci_gtd.ControlStatus+3-sizeof.uhci_gtd], 1 shl (24-24) ; set InterruptOnComplete bit |
||
3520 | clevermous | 1814 | mov eax, [esp+4] |
3653 | clevermous | 1815 | or byte [eax+uhci_gtd.ControlStatus+2-sizeof.uhci_gtd], 1 shl (23-16) ; set Active bit |
3656 | clevermous | 1816 | test [ebx+usb_pipe.Type], 1 |
1817 | jnz @f |
||
3653 | clevermous | 1818 | or byte [eax+uhci_gtd.NextTD-sizeof.uhci_gtd], 4 ; set Depth bit |
3656 | clevermous | 1819 | @@: |
3520 | clevermous | 1820 | ret |
1821 | endp |
||
1822 | |||
4418 | clevermous | 1823 | ; Allocates one endpoint structure for OHCI. |
1824 | ; Returns pointer to software part (usb_pipe) in eax. |
||
1825 | proc uhci_alloc_pipe |
||
1826 | push ebx |
||
1827 | mov ebx, uhci_ep_mutex |
||
1828 | invoke usbhc_api.usb_allocate_common, (sizeof.uhci_pipe + sizeof.usb_pipe + 0Fh) and not 0Fh |
||
1829 | test eax, eax |
||
1830 | jz @f |
||
1831 | add eax, sizeof.uhci_pipe |
||
1832 | @@: |
||
1833 | pop ebx |
||
1834 | ret |
||
1835 | endp |
||
1836 | |||
1837 | ; Free memory associated with pipe. |
||
1838 | ; For UHCI, this includes usb_pipe structure and ErrorTD, if present. |
||
1839 | proc uhci_free_pipe |
||
1840 | mov eax, [esp+4] |
||
1841 | mov eax, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe] |
||
1842 | test eax, eax |
||
1843 | jz @f |
||
1844 | stdcall uhci_free_td, eax |
||
1845 | @@: |
||
1846 | sub dword [esp+4], sizeof.uhci_pipe |
||
1847 | jmp [usbhc_api.usb_free_common] |
||
1848 | endp |
||
1849 | |||
1850 | ; Allocates one general transfer descriptor structure for UHCI. |
||
1851 | ; Returns pointer to software part (usb_gtd) in eax. |
||
1852 | proc uhci_alloc_td |
||
1853 | push ebx |
||
1854 | mov ebx, uhci_gtd_mutex |
||
1855 | invoke usbhc_api.usb_allocate_common, (sizeof.uhci_gtd + sizeof.usb_gtd + 0Fh) and not 0Fh |
||
1856 | test eax, eax |
||
1857 | jz @f |
||
1858 | add eax, sizeof.uhci_gtd |
||
1859 | @@: |
||
1860 | pop ebx |
||
1861 | ret |
||
1862 | endp |
||
1863 | |||
3520 | clevermous | 1864 | ; Free all memory associated with one TD. |
1865 | ; For UHCI, this includes memory for uhci_gtd itself |
||
1866 | ; and the temporary buffer, if present. |
||
1867 | proc uhci_free_td |
||
1868 | mov eax, [esp+4] |
||
3653 | clevermous | 1869 | mov eax, [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] |
3520 | clevermous | 1870 | and eax, not 1 |
1871 | jz .nobuf |
||
4418 | clevermous | 1872 | invoke Kfree |
3520 | clevermous | 1873 | .nobuf: |
3653 | clevermous | 1874 | sub dword [esp+4], sizeof.uhci_gtd |
4418 | clevermous | 1875 | jmp [usbhc_api.usb_free_common] |
3520 | clevermous | 1876 | endp |
4418 | clevermous | 1877 | |
1878 | include 'usb1_scheduler.inc' |
||
1879 | define_controller_name uhci |
||
1880 | |||
1881 | section '.data' readable writable |
||
1882 | include '../peimport.inc' |
||
1883 | include_debug_strings |
||
1884 | IncludeIGlobals |
||
1885 | IncludeUGlobals |
||
1886 | align 4 |
||
1887 | usbhc_api usbhc_func |
||
1888 | uhci_ep_first_page dd ? |
||
1889 | uhci_ep_mutex MUTEX |
||
1890 | uhci_gtd_first_page dd ? |
||
1891 | uhci_gtd_mutex MUTEX=>>>>> |