81,27 → 81,21 |
; * The hardware requires 32-bytes alignment of the hardware part, so |
; the entire descriptor must be 32-bytes aligned. Since the allocator |
; (usb_allocate_common) allocates memory sequentially from page start |
; (aligned on 0x1000 bytes), size of the structure must be divisible by 32. |
; (aligned on 0x1000 bytes), block size for the allocator must be divisible |
; by 32; ehci_alloc_td ensures this. |
; * The hardware also requires that the hardware part must not cross page |
; boundary; the allocator satisfies this automatically. |
struct ehci_gtd ehci_hardware_td |
Flags dd ? |
; Copy of flags from the call to usb_*_transfer_async. |
SoftwarePart rd sizeof.usb_gtd/4 |
; Software part, common for all controllers. |
rd 3 ; padding |
ends |
|
if sizeof.ehci_gtd mod 32 |
.err ehci_gtd must be 32-bytes aligned |
end if |
|
; EHCI-specific part of a pipe descriptor. |
; * This structure corresponds to the Queue Head from the EHCI specification. |
; * The hardware requires 32-bytes alignment of the hardware part. |
; Since the allocator (usb_allocate_common) allocates memory sequentially |
; from page start (aligned on 0x1000 bytes), size of the structure must be |
; divisible by 32. |
; from page start (aligned on 0x1000 bytes), block size for the allocator |
; must be divisible by 32; ehci_alloc_pipe ensures this. |
; * The hardware requires also that the hardware part must not cross page |
; boundary; the allocator satisfies this automatically. |
struct ehci_pipe |
154,15 → 148,8 |
; from the new TD, if any. |
BaseList dd ? |
; Pointer to head of the corresponding pipe list. |
SoftwarePart rd sizeof.usb_pipe/4 |
; Software part, common for all controllers. |
rd 2 ; padding |
ends |
|
if sizeof.ehci_pipe mod 32 |
.err ehci_pipe must be 32-bytes aligned |
end if |
|
; This structure describes the static head of every list of pipes. |
; The hardware requires 32-bytes alignment of this structure. |
; All instances of this structure are located sequentially in ehci_controller, |
764,7 → 751,7 |
; and stores USB device address in the ehci_pipe structure. |
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address |
proc ehci_set_device_address |
mov byte [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart], cl |
mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl |
call usb_subscribe_control |
ret |
endp |
773,7 → 760,7 |
; in: esi -> usb_controller, ebx -> usb_pipe |
; out: eax = endpoint address |
proc ehci_get_device_address |
mov eax, [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart] |
mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
and eax, 7Fh |
ret |
endp |
793,11 → 780,11 |
; stores the packet size in ehci_pipe structure. |
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size |
proc ehci_set_endpoint_packet_size |
mov eax, [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart] |
mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
and eax, not (0x7FF shl 16) |
shl ecx, 16 |
or eax, ecx |
mov [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart], eax |
mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax |
; Wait until hardware cache is evicted. |
call usb_subscribe_control |
ret |
818,10 → 805,10 |
proc ehci_alloc_pipe |
push ebx |
mov ebx, ehci_ep_mutex |
stdcall usb_allocate_common, sizeof.ehci_pipe |
stdcall usb_allocate_common, (sizeof.ehci_pipe + sizeof.usb_pipe + 1Fh) and not 1Fh |
test eax, eax |
jz @f |
add eax, ehci_pipe.SoftwarePart |
add eax, sizeof.ehci_pipe |
@@: |
pop ebx |
ret |
834,7 → 821,7 |
dd ? ; return address |
.ptr dd ? |
end virtual |
sub [.ptr], ehci_pipe.SoftwarePart |
sub [.ptr], sizeof.ehci_pipe |
jmp usb_free_common |
endp |
|
853,9 → 840,9 |
end virtual |
; 1. Zero all fields in the hardware part. |
push eax ecx |
sub edi, ehci_pipe.SoftwarePart |
sub edi, sizeof.ehci_pipe |
xor eax, eax |
movi ecx, ehci_pipe.SoftwarePart/4 |
movi ecx, sizeof.ehci_pipe/4 |
rep stosd |
pop ecx eax |
; 2. Setup PID in the first TD and make sure that the it is not active. |
862,16 → 849,16 |
xor edx, edx |
test byte [.endpoint], 80h |
setnz dh |
mov [eax+ehci_gtd.Token-ehci_gtd.SoftwarePart], edx |
mov [eax+ehci_gtd.NextTD-ehci_gtd.SoftwarePart], 1 |
mov [eax+ehci_gtd.AlternateNextTD-ehci_gtd.SoftwarePart], 1 |
mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
; 3. Store physical address of the first TD. |
sub eax, ehci_gtd.SoftwarePart |
sub eax, sizeof.ehci_gtd |
call get_phys_addr |
mov [edi+ehci_pipe.Overlay.NextTD-ehci_pipe.SoftwarePart], eax |
mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
; 4. Fill ehci_pipe.Flags except for S- and C-masks. |
; Copy location from the config pipe. |
mov eax, [ecx+ehci_pipe.Flags-ehci_pipe.SoftwarePart] |
mov eax, [ecx+ehci_pipe.Flags-sizeof.ehci_pipe] |
and eax, 3FFF0000h |
; Use 1 requests per microframe for control/bulk endpoints, |
; use value from the endpoint descriptor for periodic endpoints |
884,9 → 871,9 |
@@: |
shl edx, 30 |
or eax, edx |
mov [edi+ehci_pipe.Flags-ehci_pipe.SoftwarePart], eax |
mov [edi+ehci_pipe.Flags-sizeof.ehci_pipe], eax |
; 5. Fill ehci_pipe.Token. |
mov eax, [ecx+ehci_pipe.Token-ehci_pipe.SoftwarePart] |
mov eax, [ecx+ehci_pipe.Token-sizeof.ehci_pipe] |
; copy following fields from the config pipe: |
; DeviceAddress, EndpointSpeed, ControlEndpoint if new type is control |
mov ecx, eax |
915,7 → 902,7 |
@@: |
or eax, 40000000h |
.nonak: |
mov [edi+ehci_pipe.Token-ehci_pipe.SoftwarePart], eax |
mov [edi+ehci_pipe.Token-sizeof.ehci_pipe], eax |
; 5. Select the corresponding list and insert to the list. |
; 5a. Use Control list for control pipes, Bulk list for bulk pipes. |
lea edx, [esi+ehci_controller.ControlED.SoftwarePart-sizeof.ehci_controller] |
931,7 → 918,7 |
; another for split transactions. |
; This could fail if the requested bandwidth is not available; |
; if so, return an error. |
test word [edi+ehci_pipe.Flags-ehci_pipe.SoftwarePart+2], 3FFFh |
test word [edi+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
jnz .interrupt_fs |
call ehci_select_hs_interrupt_list |
jmp .interrupt_common |
940,9 → 927,9 |
.interrupt_common: |
test edx, edx |
jz .return0 |
mov word [edi+ehci_pipe.Flags-ehci_pipe.SoftwarePart], ax |
mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax |
.insert: |
mov [edi+ehci_pipe.BaseList-ehci_pipe.SoftwarePart], edx |
mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx |
; Insert to the head of the corresponding list. |
; Note: inserting to the head guarantees that the list traverse in |
; ehci_process_updated_schedule, once started, will not interact with new pipes. |
957,8 → 944,8 |
; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, |
; store the physical address of the new pipe to previous NextQH. |
mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] |
mov [edi+ehci_pipe.NextQH-ehci_pipe.SoftwarePart], ecx |
lea eax, [edi-ehci_pipe.SoftwarePart] |
mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx |
lea eax, [edi-sizeof.ehci_pipe] |
call get_phys_addr |
inc eax |
inc eax |
1058,15 → 1045,21 |
; this corresponds to 4001h bytes. If the requested size is |
; greater, we should split the transfer into several descriptors. |
; Boundaries to split must be multiples of endpoint transfer size |
; to avoid short packets except in the end of the transfer, |
; 4000h is always a good value. |
; to avoid short packets except in the end of the transfer. |
cmp [size], 4001h |
jbe .lastpacket |
; 2. While the remaining data cannot fit in one descriptor, |
; allocate full descriptors (of maximal possible size). |
mov edi, 4000h |
; 2a. Calculate size of one descriptor: must be a multiple of transfer size |
; and must be not greater than 4001h. |
movzx ecx, word [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe] |
mov eax, 4001h |
xor edx, edx |
mov edi, eax |
div ecx |
sub edi, edx |
mov [packetSize], edi |
.fullpackets: |
cmp [size], edi |
jbe .lastpacket |
call ehci_alloc_packet |
test eax, eax |
jz .fail |
1073,7 → 1066,8 |
mov [td], eax |
add [buffer], edi |
sub [size], edi |
jmp .fullpackets |
cmp [size], 4001h |
ja .fullpackets |
; 3. The remaining data can fit in one packet; |
; allocate the last descriptor with size = size of remaining data. |
.lastpacket: |
1084,7 → 1078,7 |
jz .fail |
; 9. Update flags in the last packet. |
mov edx, [flags] |
mov [ecx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], edx |
mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], edx |
; 10. Fill AlternateNextTD field in all allocated TDs. |
; If the caller says that short transfer is ok, the queue must advance to |
; the next descriptor, which is in eax. |
1093,7 → 1087,7 |
push eax |
test dl, 1 |
jz .disable_short |
sub eax, ehci_gtd.SoftwarePart |
sub eax, sizeof.ehci_gtd |
jmp @f |
.disable_short: |
mov eax, [ebx+usb_pipe.Controller] |
1104,7 → 1098,7 |
@@: |
cmp edx, [esp] |
jz @f |
mov [edx+ehci_gtd.AlternateNextTD-ehci_gtd.SoftwarePart], eax |
mov [edx+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], eax |
mov edx, [edx+usb_gtd.NextVirt] |
jmp @b |
@@: |
1144,28 → 1138,28 |
call usb_init_transfer |
pop eax |
; 3. Copy PID to the new descriptor. |
mov edx, [ecx+ehci_gtd.Token-ehci_gtd.SoftwarePart] |
mov [eax+ehci_gtd.Token-ehci_gtd.SoftwarePart], edx |
mov [eax+ehci_gtd.NextTD-ehci_gtd.SoftwarePart], 1 |
mov [eax+ehci_gtd.AlternateNextTD-ehci_gtd.SoftwarePart], 1 |
mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
; 4. Save the returned value (next descriptor). |
push eax |
; 5. Store the physical address of the next descriptor. |
sub eax, ehci_gtd.SoftwarePart |
sub eax, sizeof.ehci_gtd |
call get_phys_addr |
mov [ecx+ehci_gtd.NextTD-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax |
; 6. For zero-length transfers, store zero in all fields for buffer addresses. |
; Otherwise, fill them with real values. |
xor eax, eax |
mov [ecx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], eax |
repeat 10 |
mov [ecx+ehci_gtd.BufferPointers-ehci_gtd.SoftwarePart+(%-1)*4], eax |
mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd+(%-1)*4], eax |
end repeat |
cmp [.packetSize], eax |
jz @f |
mov eax, [.buffer] |
call get_phys_addr |
mov [ecx+ehci_gtd.BufferPointers-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax |
and eax, 0xFFF |
mov edx, [.packetSize] |
add edx, eax |
1174,25 → 1168,25 |
mov eax, [.buffer] |
add eax, 0x1000 |
call get_pg_addr |
mov [ecx+ehci_gtd.BufferPointers+4-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax |
sub edx, 0x1000 |
jbe @f |
mov eax, [.buffer] |
add eax, 0x2000 |
call get_pg_addr |
mov [ecx+ehci_gtd.BufferPointers+8-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax |
sub edx, 0x1000 |
jbe @f |
mov eax, [.buffer] |
add eax, 0x3000 |
call get_pg_addr |
mov [ecx+ehci_gtd.BufferPointers+12-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax |
sub edx, 0x1000 |
jbe @f |
mov eax, [.buffer] |
add eax, 0x4000 |
call get_pg_addr |
mov [ecx+ehci_gtd.BufferPointers+16-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax |
@@: |
; 7. Fill Token field: |
; set Status = 0 (inactive, ehci_insert_transfer would mark everything active); |
1202,7 → 1196,7 |
; set current page to 0; |
; do not interrupt on complete (ehci_insert_transfer sets this bit where needed); |
; set DataToggle to bit 2 of [.direction]. |
mov eax, [ecx+ehci_gtd.Token-ehci_gtd.SoftwarePart] |
mov eax, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
and eax, 300h ; keep PID code |
mov edx, [.direction] |
test edx, edx |
1222,7 → 1216,7 |
mov edx, [.packetSize] |
shl edx, 16 |
or eax, edx |
mov [ecx+ehci_gtd.Token-ehci_gtd.SoftwarePart], eax |
mov [ecx+ehci_gtd.Token-sizeof.ehci_gtd], eax |
; 4. Restore the returned value saved in step 2. |
pop eax |
.nothing: |
1234,10 → 1228,10 |
; ehci_alloc_transfer. |
; ecx -> last descriptor for the transfer, ebx -> usb_pipe |
proc ehci_insert_transfer |
or byte [ecx+ehci_gtd.Token+1-ehci_gtd.SoftwarePart], 80h ; set IOC bit |
or byte [ecx+ehci_gtd.Token+1-sizeof.ehci_gtd], 80h ; set IOC bit |
mov eax, [esp+4] |
.activate: |
or byte [eax+ehci_gtd.Token-ehci_gtd.SoftwarePart], 80h ; set Active bit |
or byte [eax+ehci_gtd.Token-sizeof.ehci_gtd], 80h ; set Active bit |
cmp eax, ecx |
mov eax, [eax+usb_gtd.NextVirt] |
jnz .activate |
1328,7 → 1322,7 |
.found_hs_hub: |
mov edx, [edx+usb_hub.ConfigPipe] |
inc ecx |
mov edx, [edx+ehci_pipe.Token-ehci_pipe.SoftwarePart] |
mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] |
shl ecx, 23 |
and edx, 7Fh |
shl edx, 16 |
1338,15 → 1332,15 |
.common: |
; 5. Create pseudo-pipe in the stack. |
; See ehci_init_pipe: only .Controller, .Token, .Flags fields are used. |
push esi ; ehci_pipe.SoftwarePart.Controller |
push esi ; usb_pipe.Controller |
mov ecx, esp |
sub esp, ehci_pipe.SoftwarePart - ehci_pipe.Flags - 4 |
sub esp, sizeof.ehci_pipe - ehci_pipe.Flags - 4 |
push edx ; ehci_pipe.Flags |
push eax ; ehci_pipe.Token |
; 6. Notify the protocol layer. |
call usb_new_device |
; 7. Cleanup the stack after step 5 and return. |
add esp, ehci_pipe.SoftwarePart - ehci_pipe.Flags + 8 |
add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 |
pop ecx ebx ; restore used registers |
ret |
endp |
1658,7 → 1652,7 |
; if either of conditions holds, exit from the internal loop. |
cmp ebx, [esp] |
jz .tddone |
cmp byte [ebx+ehci_gtd.Token-ehci_gtd.SoftwarePart], 0 |
cmp byte [ebx+ehci_gtd.Token-sizeof.ehci_gtd], 0 |
js .tddone |
; Release the queue lock while processing one descriptor: |
; callback function could (and often would) schedule another transfer. |
1690,7 → 1684,7 |
; cmp [eax+usb_pipe.Type], INTERRUPT_PIPE |
; jnz @f |
; DEBUGF 1,'K : finalized TD for pipe %x:\n',eax |
; lea eax, [ebx-ehci_gtd.SoftwarePart] |
; lea eax, [ebx-sizeof.ehci_gtd] |
; DEBUGF 1,'K : %x %x %x %x\n',[eax],[eax+4],[eax+8],[eax+12] |
; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28] |
;@@: |
1697,7 → 1691,7 |
; 1. Remove this descriptor from the list of descriptors for this pipe. |
call usb_unlink_td |
; 2. Calculate actual number of bytes transferred. |
mov eax, [ebx+ehci_gtd.Token-ehci_gtd.SoftwarePart] |
mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] |
lea edx, [eax+eax] |
shr edx, 17 |
sub edx, [ebx+usb_gtd.Length] |
1715,7 → 1709,7 |
xor ecx, ecx |
test al, 40h |
jnz .error |
test byte [ebx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], 1 |
test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
jnz .notify |
cmp edx, [ebx+usb_gtd.Length] |
jnz .special |
1745,7 → 1739,7 |
ret |
.error: |
push ebx |
sub ebx, ehci_gtd.SoftwarePart |
sub ebx, sizeof.ehci_gtd |
DEBUGF 1,'K : TD failed:\n' |
DEBUGF 1,'K : %x %x %x %x\n',[ebx],[ebx+4],[ebx+8],[ebx+12] |
DEBUGF 1,'K : %x %x %x %x\n',[ebx+16],[ebx+20],[ebx+24],[ebx+28] |
1752,7 → 1746,7 |
pop ebx |
DEBUGF 1,'K : pipe now:\n' |
mov ecx, [ebx+usb_gtd.Pipe] |
sub ecx, ehci_pipe.SoftwarePart |
sub ecx, sizeof.ehci_pipe |
DEBUGF 1,'K : %x %x %x %x\n',[ecx],[ecx+4],[ecx+8],[ecx+12] |
DEBUGF 1,'K : %x %x %x %x\n',[ecx+16],[ecx+20],[ecx+24],[ecx+28] |
DEBUGF 1,'K : %x %x %x %x\n',[ecx+32],[ecx+36],[ecx+40],[ecx+44] |
1802,7 → 1796,7 |
; it is not an error; in this case, go to 4 with ecx = 0. |
cmp ecx, USB_STATUS_UNDERRUN |
jnz @f |
test byte [ebx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], 1 |
test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
jz @f |
xor ecx, ecx |
pop edx ; length |
1832,20 → 1826,20 |
; to the next transfer. (According to the standard, "A control pipe may also |
; support functional stall as well, but this is not recommended."). |
mov edx, [ebx+usb_gtd.Pipe] |
mov eax, [ebx+ehci_gtd.NextTD-ehci_gtd.SoftwarePart] |
mov eax, [ebx+ehci_gtd.NextTD-sizeof.ehci_gtd] |
or al, 1 |
mov [edx+ehci_pipe.Overlay.NextTD-ehci_pipe.SoftwarePart], eax |
mov [edx+ehci_pipe.Overlay.AlternateNextTD-ehci_pipe.SoftwarePart], eax |
mov [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
mov [edx+ehci_pipe.Overlay.AlternateNextTD-sizeof.ehci_pipe], eax |
cmp [edx+usb_pipe.Type], CONTROL_PIPE |
jz .control |
; Bulk/interrupt transfer; halt the queue. |
mov [edx+ehci_pipe.Overlay.Token-ehci_pipe.SoftwarePart], 40h |
mov [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 40h |
pop edx |
jmp .notify |
; Control transfer. |
.control: |
and [edx+ehci_pipe.Overlay.Token-ehci_pipe.SoftwarePart], 0 |
dec [edx+ehci_pipe.Overlay.NextTD-ehci_pipe.SoftwarePart] |
and [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 0 |
dec [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe] |
pop edx |
jmp .notify |
endp |
1855,7 → 1849,7 |
proc ehci_unlink_pipe |
cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE |
jnz @f |
test word [ebx+ehci_pipe.Flags-ehci_pipe.SoftwarePart+2], 3FFFh |
test word [ebx+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
jnz .interrupt_fs |
call ehci_hs_interrupt_list_unlink |
jmp .interrupt_common |
1870,9 → 1864,9 |
mov edx, esi |
sub edx, eax |
cmp edx, sizeof.ehci_controller |
mov edx, [ebx+ehci_pipe.NextQH-ehci_pipe.SoftwarePart] |
mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe] |
jb .prev_is_static |
mov [eax+ehci_pipe.NextQH-ehci_pipe.SoftwarePart], edx |
mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx |
ret |
.prev_is_static: |
mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx |
1882,10 → 1876,10 |
proc ehci_alloc_td |
push ebx |
mov ebx, ehci_gtd_mutex |
stdcall usb_allocate_common, sizeof.ehci_gtd |
stdcall usb_allocate_common, (sizeof.ehci_gtd + sizeof.usb_gtd + 1Fh) and not 1Fh |
test eax, eax |
jz @f |
add eax, ehci_gtd.SoftwarePart |
add eax, sizeof.ehci_gtd |
@@: |
pop ebx |
ret |
1895,6 → 1889,6 |
; frees all additional data associated with the transfer descriptor. |
; EHCI has no additional data, so just free ehci_gtd structure. |
proc ehci_free_td |
sub dword [esp+4], ehci_gtd.SoftwarePart |
sub dword [esp+4], sizeof.ehci_gtd |
jmp usb_free_common |
endp |