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