separate USB host controller code into external drivers

git-svn-id: svn://kolibrios.org@4418 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
CleverMouse 2013-12-30 11:18:33 +00:00
parent bcb5772288
commit a10422fbce
23 changed files with 1037 additions and 1171 deletions

View File

@ -147,6 +147,9 @@ FASM_PROGRAMS:=\
drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \
drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \
drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \ drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \
drivers/uhci.sys:DRIVERS/UHCI.SYS:$(REPOSITORY)/drivers/usb/uhci.asm \
drivers/ohci.sys:DRIVERS/OHCI.SYS:$(REPOSITORY)/drivers/usb/ohci.asm \
drivers/ehci.sys:DRIVERS/EHCI.SYS:$(REPOSITORY)/drivers/usb/ehci.asm \
File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \ File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \
File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \
File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \

View File

@ -147,6 +147,9 @@ FASM_PROGRAMS:=\
drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \
drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \
drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \ drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \
drivers/uhci.sys:DRIVERS/UHCI.SYS:$(REPOSITORY)/drivers/usb/uhci.asm \
drivers/ohci.sys:DRIVERS/OHCI.SYS:$(REPOSITORY)/drivers/usb/ohci.asm \
drivers/ehci.sys:DRIVERS/EHCI.SYS:$(REPOSITORY)/drivers/usb/ehci.asm \
File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \ File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \
File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \
File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \

View File

@ -147,6 +147,9 @@ FASM_PROGRAMS:=\
drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \
drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \
drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \ drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \
drivers/uhci.sys:DRIVERS/UHCI.SYS:$(REPOSITORY)/drivers/usb/uhci.asm \
drivers/ohci.sys:DRIVERS/OHCI.SYS:$(REPOSITORY)/drivers/usb/ohci.asm \
drivers/ehci.sys:DRIVERS/EHCI.SYS:$(REPOSITORY)/drivers/usb/ehci.asm \
File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \ File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \
File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \
File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \

View File

@ -148,6 +148,9 @@ FASM_PROGRAMS:=\
drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \
drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \
drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \ drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \
drivers/uhci.sys:DRIVERS/UHCI.SYS:$(REPOSITORY)/drivers/usb/uhci.asm \
drivers/ohci.sys:DRIVERS/OHCI.SYS:$(REPOSITORY)/drivers/usb/ohci.asm \
drivers/ehci.sys:DRIVERS/EHCI.SYS:$(REPOSITORY)/drivers/usb/ehci.asm \
File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \ File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \
File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \
File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \

View File

@ -147,6 +147,9 @@ FASM_PROGRAMS:=\
drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \
drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.asm \
drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \ drivers/sis900.obj:DRIVERS/SIS900.obj:$(REPOSITORY)/drivers/ethernet/sis900.asm \
drivers/uhci.sys:DRIVERS/UHCI.SYS:$(REPOSITORY)/drivers/usb/uhci.asm \
drivers/ohci.sys:DRIVERS/OHCI.SYS:$(REPOSITORY)/drivers/usb/ohci.asm \
drivers/ehci.sys:DRIVERS/EHCI.SYS:$(REPOSITORY)/drivers/usb/ehci.asm \
File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \ File|Managers/kfar:File|Managers/KFAR:$(PROGS)/fs/kfar/trunk/kfar.asm \
File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \
File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \

View File

@ -236,9 +236,11 @@ debug_beginf
pushad pushad
movzx ecx,al movzx ecx,al
mov ebx,1 mov ebx,1
; mov ecx,sys_msg_board if defined SysMsgBoard._pe_import
; call ecx ; sys_msg_board invoke SysMsgBoard
else
stdcall SysMsgBoard stdcall SysMsgBoard
end if
popad popad
ret ret
debug_endf debug_endf
@ -251,9 +253,11 @@ debug_beginf
movzx ecx,byte[edx] movzx ecx,byte[edx]
or cl,cl or cl,cl
jz .l2 jz .l2
; mov ecx,sys_msg_board if defined SysMsgBoard._pe_import
; call ecx ; sys_msg_board invoke SysMsgBoard
else
stdcall SysMsgBoard stdcall SysMsgBoard
end if
inc edx inc edx
jmp .l1 jmp .l1
.l2: ret .l2: ret

View File

@ -1,7 +1,17 @@
; Code for EHCI controllers. ; Code for EHCI controllers.
; Note: it should be moved to an external driver,
; it was convenient to have this code compiled into the kernel during initial ; Standard driver stuff
; development, but there are no reasons to keep it here. format PE DLL native
entry start
__DEBUG__ equ 1
__DEBUG_LEVEL__ equ 1
section '.reloc' data readable discardable fixups
section '.text' code readable executable
include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../../kernel/trunk/bus/usb/common.inc'
; ============================================================================= ; =============================================================================
; ================================= Constants ================================= ; ================================= Constants =================================
@ -246,6 +256,11 @@ CapabilityParams dd ?
DeferredActions dd ? DeferredActions dd ?
; Bitmask of events from EhciStatusReg which were observed by the IRQ handler ; Bitmask of events from EhciStatusReg which were observed by the IRQ handler
; and needs to be processed in the IRQ thread. ; and needs to be processed in the IRQ thread.
PortRoutes rb 16
; Companion port route description.
; Each byte describes routing of one port, value = PCI function.
; This field must be the last one:
; UHCI/OHCI code uses this field without knowing the entire structure.
ends ends
if ehci_controller.IntEDs mod 32 if ehci_controller.IntEDs mod 32
@ -258,8 +273,10 @@ end if
iglobal iglobal
align 4 align 4
ehci_hardware_func: ehci_hardware_func:
dd USBHC_VERSION
dd 'EHCI' dd 'EHCI'
dd sizeof.ehci_controller dd sizeof.ehci_controller
dd ehci_kickoff_bios
dd ehci_init dd ehci_init
dd ehci_process_deferred dd ehci_process_deferred
dd ehci_set_device_address dd ehci_set_device_address
@ -276,12 +293,39 @@ ehci_hardware_func:
dd ehci_alloc_transfer dd ehci_alloc_transfer
dd ehci_insert_transfer dd ehci_insert_transfer
dd ehci_new_device dd ehci_new_device
ehci_name db 'EHCI',0
endg endg
; ============================================================================= ; =============================================================================
; =================================== Code ==================================== ; =================================== Code ====================================
; ============================================================================= ; =============================================================================
; Called once when driver is loading and once at shutdown.
; When loading, must initialize itself, register itself in the system
; and return eax = value obtained when registering.
proc start
virtual at esp
dd ? ; return address
.reason dd ? ; DRV_ENTRY or DRV_EXIT
.cmdline dd ? ; normally NULL
end virtual
cmp [.reason], DRV_ENTRY
jnz .nothing
mov ecx, ehci_ep_mutex
invoke MutexInit
mov ecx, ehci_gtd_mutex
invoke MutexInit
push esi edi
mov esi, [USBHCFunc]
mov edi, usbhc_api
movi ecx, sizeof.usbhc_func/4
rep movsd
pop edi esi
invoke RegUSBDriver, ehci_name, 0, ehci_hardware_func
.nothing:
ret
endp
; Controller-specific initialization function. ; Controller-specific initialization function.
; Called from usb_init_controller. Initializes the hardware and ; Called from usb_init_controller. Initializes the hardware and
; EHCI-specific parts of software structures. ; EHCI-specific parts of software structures.
@ -313,7 +357,7 @@ if (ehci_controller.IntEDs mod 0x1000) <> 0
.err assertion failed .err assertion failed
end if end if
add eax, ehci_controller.IntEDs add eax, ehci_controller.IntEDs
call get_phys_addr call [GetPhysAddr]
; 2b. Fill first 32 entries. ; 2b. Fill first 32 entries.
inc eax inc eax
inc eax ; set Type to EHCI_TYPE_QH inc eax ; set Type to EHCI_TYPE_QH
@ -372,24 +416,15 @@ end if
call ehci_init_static_endpoint call ehci_init_static_endpoint
; 4. Create a virtual memory area to talk with the controller. ; 4. Create a virtual memory area to talk with the controller.
; 4a. Enable memory & bus master access. ; 4a. Enable memory & bus master access.
mov ch, [.bus] invoke PciRead16, dword [.bus], dword [.devfn], 4
mov cl, 1
mov eax, ecx
mov bh, [.devfn]
mov bl, 4
call pci_read_reg
or al, 6 or al, 6
xchg eax, ecx invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax
call pci_write_reg
; 4b. Read memory base address. ; 4b. Read memory base address.
mov ah, [.bus] invoke PciRead32, dword [.bus], dword [.devfn], 10h
mov al, 2
mov bl, 10h
call pci_read_reg
; DEBUGF 1,'K : phys MMIO %x\n',eax ; DEBUGF 1,'K : phys MMIO %x\n',eax
and al, not 0Fh and al, not 0Fh
; 4c. Create mapping for physical memory. 200h bytes are always sufficient. ; 4c. Create mapping for physical memory. 200h bytes are always sufficient.
stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE invoke MapIoMem, eax, 200h, PG_SW+PG_NOCACHE
test eax, eax test eax, eax
jz .fail jz .fail
; DEBUGF 1,'K : MMIO %x\n',eax ; DEBUGF 1,'K : MMIO %x\n',eax
@ -397,9 +432,71 @@ if ehci_controller.MMIOBase1 <> ehci_controller.BulkED + sizeof.ehci_static_ep
.err assertion failed .err assertion failed
end if end if
stosd ; fill ehci_controller.MMIOBase1 stosd ; fill ehci_controller.MMIOBase1
; 5. Read basic parameters of the controller.
; 5a. Structural parameters.
mov ebx, [eax+EhciStructParamsReg]
; 5b. Port routing rules.
; If bit 7 in HCSPARAMS is set, read and unpack EhciPortRouteReg.
; Otherwise, bits 11:8 are N_PCC = number of ports per companion,
; bits 15:12 are number of companions, maybe zero,
; first N_PCC ports are routed to the first companion and so on.
xor esi, esi
test bl, bl
js .read_routes
test bh, 0x0F
jz .no_companions
test bh, 0xF0
jz .no_companions
xor edx, edx
.fill_routes:
movzx ecx, bh
and ecx, 15
@@:
mov byte [edi+esi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)], dl
inc esi
cmp esi, 16
jz .routes_filled
dec ecx
jnz @b
movzx ecx, bh
shr ecx, 4
inc edx
cmp edx, ecx
jb .fill_routes
.no_companions:
mov byte [edi+esi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)], 0xFF
inc esi
cmp esi, 16
jnz .no_companions
jmp .routes_filled
.read_routes:
rept 2 counter
{
mov ecx, [eax+EhciPortRouteReg+(counter-1)*4]
@@:
mov edx, ecx
shr ecx, 4
and edx, 15
mov byte [edi+esi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)], dl
inc esi
cmp esi, 8*counter
jnz @b
}
.routes_filled:
; DEBUGF 1,'K : EhciPortRouteReg: %x %x\n',[eax+EhciPortRouteReg],[eax+EhciPortRouteReg+4]
; DEBUGF 1,'K : routes:\nK : '
;rept 8 counter
;{
; DEBUGF 1,' %x',[edi+ehci_controller.PortRoutes-(ehci_controller.MMIOBase1+4)+counter-1]:2
;}
; DEBUGF 1,'\nK : '
;rept 8 counter
;{
; DEBUGF 1,' %x',[edi+ehci_controller.PortRoutes+8-(ehci_controller.MMIOBase1+4)+counter-1]:2
;}
; DEBUGF 1,'\n'
movzx ecx, byte [eax+EhciCapLengthReg] movzx ecx, byte [eax+EhciCapLengthReg]
mov edx, [eax+EhciCapParamsReg] mov edx, [eax+EhciCapParamsReg]
mov ebx, [eax+EhciStructParamsReg]
add eax, ecx add eax, ecx
if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4 if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4
.err assertion failed .err assertion failed
@ -426,7 +523,7 @@ end if
and dword [edi+EhciCommandReg], not 1 and dword [edi+EhciCommandReg], not 1
@@: @@:
movi esi, 1 movi esi, 1
call delay_ms invoke Sleep
test dword [edi+EhciStatusReg], 1 shl 12 test dword [edi+EhciStatusReg], 1 shl 12
jnz .stopped jnz .stopped
loop @b loop @b
@ -438,7 +535,7 @@ end if
movi ecx, 50 movi ecx, 50
@@: @@:
movi esi, 1 movi esi, 1
call delay_ms invoke Sleep
test dword [edi+EhciCommandReg], 2 test dword [edi+EhciCommandReg], 2
jz .reset_ok jz .reset_ok
loop @b loop @b
@ -455,25 +552,21 @@ end if
mov dword [edi+EhciCtrlDataSegReg], 0 mov dword [edi+EhciCtrlDataSegReg], 0
@@: @@:
; 7b. Hook interrupt and enable appropriate interrupt sources. ; 7b. Hook interrupt and enable appropriate interrupt sources.
mov ah, [.bus] invoke PciRead8, dword [.bus], dword [.devfn], 3Ch
mov al, 0
mov bh, [.devfn]
mov bl, 3Ch
call pci_read_reg
; al = IRQ ; al = IRQ
DEBUGF 1,'K : attaching to IRQ %x\n',al ; DEBUGF 1,'K : attaching to IRQ %x\n',al
movzx eax, al movzx eax, al
stdcall attach_int_handler, eax, ehci_irq, esi invoke AttachIntHandler, eax, ehci_irq, esi
; mov dword [edi+EhciStatusReg], 111111b ; clear status ; mov dword [edi+EhciStatusReg], 111111b ; clear status
; disable Frame List Rollover interrupt, enable all other sources ; disable Frame List Rollover interrupt, enable all other sources
mov dword [edi+EhciInterruptReg], 110111b mov dword [edi+EhciInterruptReg], 110111b
; 7c. Inform the controller of the address of periodic lists head. ; 7c. Inform the controller of the address of periodic lists head.
lea eax, [esi-sizeof.ehci_controller] lea eax, [esi-sizeof.ehci_controller]
call get_phys_addr invoke GetPhysAddr
mov dword [edi+EhciPeriodicListReg], eax mov dword [edi+EhciPeriodicListReg], eax
; 7d. Inform the controller of the address of asynchronous lists head. ; 7d. Inform the controller of the address of asynchronous lists head.
lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller] lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller]
call get_phys_addr invoke GetPhysAddr
mov dword [edi+EhciAsyncListReg], eax mov dword [edi+EhciAsyncListReg], eax
; 7e. Configure operational details and run the controller. ; 7e. Configure operational details and run the controller.
mov dword [edi+EhciCommandReg], \ mov dword [edi+EhciCommandReg], \
@ -498,7 +591,7 @@ end if
jz @f jz @f
push esi push esi
movi esi, 20 movi esi, 20
call delay_ms invoke Sleep
pop esi pop esi
@@: @@:
; 9. Return pointer to usb_controller. ; 9. Return pointer to usb_controller.
@ -510,7 +603,7 @@ end if
.fail_unmap: .fail_unmap:
pop eax pop eax
push eax push eax
stdcall free_kernel_space, [eax+ehci_controller.MMIOBase1] invoke FreeKernelSpace, [eax+ehci_controller.MMIOBase1]
.fail: .fail:
pop ecx pop ecx
xor eax, eax xor eax, eax
@ -529,7 +622,7 @@ proc ehci_init_static_endpoint
test esi, esi test esi, esi
jz @f jz @f
mov eax, esi mov eax, esi
call get_phys_addr invoke GetPhysAddr
inc eax inc eax
inc eax ; set Type to EHCI_TYPE_QH inc eax ; set Type to EHCI_TYPE_QH
@@: @@:
@ -537,7 +630,8 @@ proc ehci_init_static_endpoint
mov [edi+ehci_static_ep.NextList], esi mov [edi+ehci_static_ep.NextList], esi
mov byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted mov byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted
add edi, ehci_static_ep.SoftwarePart add edi, ehci_static_ep.SoftwarePart
call usb_init_static_endpoint mov eax, [USBHCFunc]
call [eax+usbhc_func.usb_init_static_endpoint]
add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart
ret ret
endp endp
@ -565,14 +659,10 @@ endp
; deal with USB devices. ; deal with USB devices.
proc ehci_kickoff_bios proc ehci_kickoff_bios
; 1. Get the physical address of MMIO registers. ; 1. Get the physical address of MMIO registers.
mov ah, [esi+PCIDEV.bus] invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 10h
mov bh, [esi+PCIDEV.devfn]
mov al, 2
mov bl, 10h
call pci_read_reg
and al, not 0Fh and al, not 0Fh
; 2. Create mapping for physical memory. 200h bytes are always sufficient. ; 2. Create mapping for physical memory. 200h bytes are always sufficient.
stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE invoke MapIoMem, eax, 200h, PG_SW+PG_NOCACHE
test eax, eax test eax, eax
jz .nothing jz .nothing
push eax ; push argument for step 8 push eax ; push argument for step 8
@ -604,10 +694,7 @@ proc ehci_kickoff_bios
test bl, 3 test bl, 3
jnz .no_capability jnz .no_capability
; In each iteration, read the current dword, ; In each iteration, read the current dword,
mov ah, [esi+PCIDEV.bus] invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx
mov al, 2
mov bh, [esi+PCIDEV.devfn]
call pci_read_reg
; check, whether the capability ID is take-ownership ID = 1, ; check, whether the capability ID is take-ownership ID = 1,
cmp al, 1 cmp al, 1
jz .found_bios_handoff jz .found_bios_handoff
@ -632,16 +719,11 @@ proc ehci_kickoff_bios
jz .has_ownership jz .has_ownership
; 4c. Request ownership. ; 4c. Request ownership.
inc ebx inc ebx
mov cl, 1 invoke PciWrite8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, 1
mov ah, [esi+PCIDEV.bus]
mov al, 0
call pci_write_reg
; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership ; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership
; requests; if so, there is no sense in waiting. ; requests; if so, there is no sense in waiting.
inc ebx inc ebx
mov ah, [esi+PCIDEV.bus] invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx
mov al, 2
call pci_read_reg
dec ebx dec ebx
dec ebx dec ebx
test ah, 20h test ah, 20h
@ -650,14 +732,12 @@ proc ehci_kickoff_bios
; If successful, go to 5. ; If successful, go to 5.
mov dword [esp], 1000 mov dword [esp], 1000
@@: @@:
mov ah, [esi+PCIDEV.bus] invoke PciRead8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx
mov al, 0
call pci_read_reg
test al, 1 test al, 1
jz .has_ownership jz .has_ownership
push esi push esi
movi esi, 1 movi esi, 1
call delay_ms invoke Sleep
pop esi pop esi
dec dword [esp] dec dword [esp]
jnz @b jnz @b
@ -665,22 +745,15 @@ proc ehci_kickoff_bios
.force_ownership: .force_ownership:
; 4f. BIOS has not responded within the timeout. ; 4f. BIOS has not responded within the timeout.
; Let's just clear BIOS ownership flag and hope that everything will be ok. ; Let's just clear BIOS ownership flag and hope that everything will be ok.
mov ah, [esi+PCIDEV.bus] invoke PciWrite8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, 0
mov al, 0
mov cl, 0
call pci_write_reg
.has_ownership: .has_ownership:
; 5. Just in case clear all SMI event sources except change-ownership. ; 5. Just in case clear all SMI event sources except change-ownership.
dbgstr 'has_ownership' dbgstr 'has_ownership'
inc ebx inc ebx
inc ebx inc ebx
mov ah, [esi+PCIDEV.bus] invoke PciRead16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx
mov al, 2
mov ecx, eax
call pci_read_reg
and ax, 2000h and ax, 2000h
xchg eax, ecx invoke PciWrite16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, eax
call pci_write_reg
.has_ownership2: .has_ownership2:
pop ecx pop ecx
; 6. Disable all controller interrupts until the system will be ready to ; 6. Disable all controller interrupts until the system will be ready to
@ -689,7 +762,7 @@ proc ehci_kickoff_bios
; 7. Now we can unblock interrupts in the processor. ; 7. Now we can unblock interrupts in the processor.
popf popf
; 8. Release memory mapping created in step 2 and return. ; 8. Release memory mapping created in step 2 and return.
call free_kernel_space invoke FreeKernelSpace
.nothing: .nothing:
ret ret
endp endp
@ -717,7 +790,6 @@ end virtual
mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller]
spin_lock_irqsave [esi+usb_controller.WaitSpinlock] spin_lock_irqsave [esi+usb_controller.WaitSpinlock]
mov eax, [edi+EhciStatusReg] mov eax, [edi+EhciStatusReg]
; DEBUGF 1,'K : [%d] EHCI status %x\n',[timer_ticks],eax
; 3. Check whether that interrupt has been generated by our controller. ; 3. Check whether that interrupt has been generated by our controller.
; (One IRQ can be shared by several devices.) ; (One IRQ can be shared by several devices.)
and eax, [edi+EhciInterruptReg] and eax, [edi+EhciInterruptReg]
@ -739,7 +811,7 @@ end virtual
or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax
spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock]
inc ebx inc ebx
call usb_wakeup_if_needed invoke usbhc_api.usb_wakeup_if_needed
; 6. Interrupt processed; return non-zero. ; 6. Interrupt processed; return non-zero.
mov al, 1 mov al, 1
pop edi esi ebx ; restore used registers to be cdecl pop edi esi ebx ; restore used registers to be cdecl
@ -751,8 +823,7 @@ endp
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
proc ehci_set_device_address proc ehci_set_device_address
mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl
call usb_subscribe_control jmp [usbhc_api.usb_subscribe_control]
ret
endp endp
; This procedure returns USB device address from the ehci_pipe structure. ; This procedure returns USB device address from the ehci_pipe structure.
@ -785,8 +856,7 @@ proc ehci_set_endpoint_packet_size
or eax, ecx or eax, ecx
mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax
; Wait until hardware cache is evicted. ; Wait until hardware cache is evicted.
call usb_subscribe_control jmp [usbhc_api.usb_subscribe_control]
ret
endp endp
uglobal uglobal
@ -804,7 +874,7 @@ endg
proc ehci_alloc_pipe proc ehci_alloc_pipe
push ebx push ebx
mov ebx, ehci_ep_mutex mov ebx, ehci_ep_mutex
stdcall usb_allocate_common, (sizeof.ehci_pipe + sizeof.usb_pipe + 1Fh) and not 1Fh invoke usbhc_api.usb_allocate_common, (sizeof.ehci_pipe + sizeof.usb_pipe + 1Fh) and not 1Fh
test eax, eax test eax, eax
jz @f jz @f
add eax, sizeof.ehci_pipe add eax, sizeof.ehci_pipe
@ -821,7 +891,7 @@ virtual at esp
.ptr dd ? .ptr dd ?
end virtual end virtual
sub [.ptr], sizeof.ehci_pipe sub [.ptr], sizeof.ehci_pipe
jmp usb_free_common jmp [usbhc_api.usb_free_common]
endp endp
; This procedure is called from API usb_open_pipe and processes ; This procedure is called from API usb_open_pipe and processes
@ -853,7 +923,7 @@ end virtual
mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1
; 3. Store physical address of the first TD. ; 3. Store physical address of the first TD.
sub eax, sizeof.ehci_gtd sub eax, sizeof.ehci_gtd
call get_phys_addr call [GetPhysAddr]
mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax
; 4. Fill ehci_pipe.Flags except for S- and C-masks. ; 4. Fill ehci_pipe.Flags except for S- and C-masks.
; Copy location from the config pipe. ; Copy location from the config pipe.
@ -945,7 +1015,7 @@ end virtual
mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart]
mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx
lea eax, [edi-sizeof.ehci_pipe] lea eax, [edi-sizeof.ehci_pipe]
call get_phys_addr call [GetPhysAddr]
inc eax inc eax
inc eax inc eax
mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax
@ -963,7 +1033,7 @@ endp
proc ehci_new_port proc ehci_new_port
; 1. If the device operates at low-speed, just release it to a companion. ; 1. If the device operates at low-speed, just release it to a companion.
mov eax, [edi+EhciPortsReg+ecx*4] mov eax, [edi+EhciPortsReg+ecx*4]
DEBUGF 1,'K : [%d] EHCI %x port %d state is %x\n',[timer_ticks],esi,ecx,eax DEBUGF 1,'K : EHCI %x port %d state is %x\n',esi,ecx,eax
mov edx, eax mov edx, eax
and ah, 0Ch and ah, 0Ch
cmp ah, 4 cmp ah, 4
@ -996,17 +1066,17 @@ proc ehci_new_port
and al, not (4 or 2Ah) and al, not (4 or 2Ah)
mov [edi+EhciPortsReg+ecx*4], eax mov [edi+EhciPortsReg+ecx*4], eax
; 3. Store the current time and set status to 1 = reset signalling active. ; 3. Store the current time and set status to 1 = reset signalling active.
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ResetTime], eax mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 1 mov [esi+usb_controller.ResettingStatus], 1
; dbgstr 'high-speed or full-speed device, resetting' ; dbgstr 'high-speed or full-speed device, resetting'
DEBUGF 1,'K : [%d] EHCI %x: port %d has HS or FS device, resetting\n',[timer_ticks],esi,ecx DEBUGF 1,'K : EHCI %x: port %d has HS or FS device, resetting\n',esi,ecx
pop edi pop edi
.nothing: .nothing:
ret ret
.low_speed: .low_speed:
; dbgstr 'low-speed device, releasing' ; dbgstr 'low-speed device, releasing'
DEBUGF 1,'K : [%d] EHCI %x: port %d has LS device, releasing\n',[timer_ticks],esi,ecx DEBUGF 1,'K : EHCI %x: port %d has LS device, releasing\n',esi,ecx
or dh, 20h or dh, 20h
and dl, not 2Ah and dl, not 2Ah
mov [edi+EhciPortsReg+ecx*4], edx mov [edi+EhciPortsReg+ecx*4], edx
@ -1051,7 +1121,8 @@ endl
; allocate full descriptors (of maximal possible size). ; allocate full descriptors (of maximal possible size).
; 2a. Calculate size of one descriptor: must be a multiple of transfer size ; 2a. Calculate size of one descriptor: must be a multiple of transfer size
; and must be not greater than 4001h. ; and must be not greater than 4001h.
movzx ecx, word [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe] movzx ecx, word [ebx+ehci_pipe.Token+2-sizeof.ehci_pipe]
and ecx, (1 shl 11) - 1
mov eax, 4001h mov eax, 4001h
xor edx, edx xor edx, edx
mov edi, eax mov edi, eax
@ -1092,7 +1163,7 @@ endl
mov eax, [ebx+usb_pipe.Controller] mov eax, [ebx+usb_pipe.Controller]
add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller
@@: @@:
call get_phys_addr call [GetPhysAddr]
mov edx, [origTD] mov edx, [origTD]
@@: @@:
cmp edx, [esp] cmp edx, [esp]
@ -1106,7 +1177,7 @@ endl
.fail: .fail:
mov edi, ehci_hardware_func mov edi, ehci_hardware_func
mov eax, [td] mov eax, [td]
stdcall usb_undo_tds, [origTD] invoke usbhc_api.usb_undo_tds, [origTD]
xor eax, eax xor eax, eax
ret ret
endp endp
@ -1134,7 +1205,7 @@ end virtual
jz .nothing jz .nothing
; 2. Initialize controller-independent parts of both TDs. ; 2. Initialize controller-independent parts of both TDs.
push eax push eax
call usb_init_transfer invoke usbhc_api.usb_init_transfer
pop eax pop eax
; 3. Copy PID to the new descriptor. ; 3. Copy PID to the new descriptor.
mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd]
@ -1145,7 +1216,7 @@ end virtual
push eax push eax
; 5. Store the physical address of the next descriptor. ; 5. Store the physical address of the next descriptor.
sub eax, sizeof.ehci_gtd sub eax, sizeof.ehci_gtd
call get_phys_addr call [GetPhysAddr]
mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax
; 6. For zero-length transfers, store zero in all fields for buffer addresses. ; 6. For zero-length transfers, store zero in all fields for buffer addresses.
; Otherwise, fill them with real values. ; Otherwise, fill them with real values.
@ -1157,7 +1228,7 @@ end repeat
cmp [.packetSize], eax cmp [.packetSize], eax
jz @f jz @f
mov eax, [.buffer] mov eax, [.buffer]
call get_phys_addr call [GetPhysAddr]
mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax
and eax, 0xFFF and eax, 0xFFF
mov edx, [.packetSize] mov edx, [.packetSize]
@ -1166,25 +1237,25 @@ end repeat
jbe @f jbe @f
mov eax, [.buffer] mov eax, [.buffer]
add eax, 0x1000 add eax, 0x1000
call get_pg_addr call [GetPgAddr]
mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax
sub edx, 0x1000 sub edx, 0x1000
jbe @f jbe @f
mov eax, [.buffer] mov eax, [.buffer]
add eax, 0x2000 add eax, 0x2000
call get_pg_addr call [GetPgAddr]
mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax
sub edx, 0x1000 sub edx, 0x1000
jbe @f jbe @f
mov eax, [.buffer] mov eax, [.buffer]
add eax, 0x3000 add eax, 0x3000
call get_pg_addr call [GetPgAddr]
mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax
sub edx, 0x1000 sub edx, 0x1000
jbe @f jbe @f
mov eax, [.buffer] mov eax, [.buffer]
add eax, 0x4000 add eax, 0x4000
call get_pg_addr call [GetPgAddr]
mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax
@@: @@:
; 7. Fill Token field: ; 7. Fill Token field:
@ -1242,10 +1313,10 @@ endp
proc ehci_port_reset_done proc ehci_port_reset_done
movzx ecx, [esi+usb_controller.ResettingPort] movzx ecx, [esi+usb_controller.ResettingPort]
and dword [edi+EhciPortsReg+ecx*4], not 12Ah and dword [edi+EhciPortsReg+ecx*4], not 12Ah
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ResetTime], eax mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 2 mov [esi+usb_controller.ResettingStatus], 2
DEBUGF 1,'K : [%d] EHCI %x: reset port %d done\n',[timer_ticks],esi,ecx ; DEBUGF 1,'K : EHCI %x: reset port %d done\n',esi,ecx
ret ret
endp endp
@ -1258,21 +1329,23 @@ proc ehci_port_init
xor eax, eax xor eax, eax
xchg al, [esi+usb_controller.ResettingStatus] xchg al, [esi+usb_controller.ResettingStatus]
test al, al test al, al
js usb_test_pending_port jns @f
jmp [usbhc_api.usb_test_pending_port]
@@:
; 2. Get the port status. High-speed devices should be now enabled, ; 2. Get the port status. High-speed devices should be now enabled,
; full-speed devices are left disabled; ; full-speed devices are left disabled;
; if the port is disabled, release it to a companion and continue to ; if the port is disabled, release it to a companion and continue to
; next device (if there is one). ; next device (if there is one).
movzx ecx, [esi+usb_controller.ResettingPort] movzx ecx, [esi+usb_controller.ResettingPort]
mov eax, [edi+EhciPortsReg+ecx*4] mov eax, [edi+EhciPortsReg+ecx*4]
DEBUGF 1,'K : [%d] EHCI %x status of port %d is %x\n',[timer_ticks],esi,ecx,eax DEBUGF 1,'K : EHCI %x status of port %d is %x\n',esi,ecx,eax
test al, 4 test al, 4
jnz @f jnz @f
; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax ; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax
dbgstr 'releasing to companion' dbgstr 'releasing to companion'
or ah, 20h or ah, 20h
mov [edi+EhciPortsReg+ecx*4], eax mov [edi+EhciPortsReg+ecx*4], eax
jmp usb_test_pending_port jmp [usbhc_api.usb_test_pending_port]
@@: @@:
; 3. Call the worker procedure to notify the protocol layer ; 3. Call the worker procedure to notify the protocol layer
; about new EHCI device. It is high-speed. ; about new EHCI device. It is high-speed.
@ -1284,7 +1357,7 @@ proc ehci_port_init
; (no memory, no bus address), disable the port and stop the initialization. ; (no memory, no bus address), disable the port and stop the initialization.
.disable_exit: .disable_exit:
and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah) and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah)
jmp usb_test_pending_port jmp [usbhc_api.usb_test_pending_port]
.nothing: .nothing:
ret ret
endp endp
@ -1307,20 +1380,10 @@ proc ehci_new_device
; of the last high-speed hub (the closest to the device hub) ; of the last high-speed hub (the closest to the device hub)
; for split transactions, and set ControlEndpoint bit in eax; ; for split transactions, and set ControlEndpoint bit in eax;
; ehci_init_pipe assumes that the parent pipe is a control pipe. ; ehci_init_pipe assumes that the parent pipe is a control pipe.
push eax
movzx ecx, [esi+usb_controller.ResettingPort] movzx ecx, [esi+usb_controller.ResettingPort]
mov edx, [esi+usb_controller.ResettingHub] mov edx, [esi+usb_controller.ResettingHub]
; If the parent hub is high-speed, it is TT for the device. invoke usbhc_api.usb_get_tt
; Otherwise, the parent hub itself is behind TT, and the device
; has the same TT hub+port as the parent hub.
push eax
mov eax, [edx+usb_hub.ConfigPipe]
mov eax, [eax+usb_pipe.DeviceData]
cmp [eax+usb_device_data.Speed], USB_SPEED_HS
jz @f
movzx ecx, [eax+usb_device_data.TTPort]
mov edx, [eax+usb_device_data.TTHub]
@@:
mov edx, [edx+usb_hub.ConfigPipe]
inc ecx inc ecx
mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe]
shl ecx, 23 shl ecx, 23
@ -1338,7 +1401,7 @@ proc ehci_new_device
push edx ; ehci_pipe.Flags push edx ; ehci_pipe.Flags
push eax ; ehci_pipe.Token push eax ; ehci_pipe.Token
; 6. Notify the protocol layer. ; 6. Notify the protocol layer.
call usb_new_device invoke usbhc_api.usb_new_device
; 7. Cleanup the stack after step 5 and return. ; 7. Cleanup the stack after step 5 and return.
add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8
pop ecx ebx ; restore used registers pop ecx ebx ; restore used registers
@ -1378,7 +1441,7 @@ proc ehci_process_deferred
mov [edi+EhciPortsReg+ecx*4], eax mov [edi+EhciPortsReg+ecx*4], eax
mov ebx, eax mov ebx, eax
mov eax, [edi+EhciPortsReg+ecx*4] mov eax, [edi+EhciPortsReg+ecx*4]
DEBUGF 1,'K : [%d] EHCI %x: status of port %d changed to %x\n',[timer_ticks],esi,ecx,ebx DEBUGF 1,'K : EHCI %x: status of port %d changed to %x\n',esi,ecx,ebx
; 3e. Handle overcurrent. ; 3e. Handle overcurrent.
; Note: that needs work. ; Note: that needs work.
test bl, 20h ; overcurrent change test bl, 20h ; overcurrent change
@ -1411,7 +1474,7 @@ proc ehci_process_deferred
; USB_CONNECT_DELAY ticks. ; USB_CONNECT_DELAY ticks.
test al, 1 test al, 1
jz .disconnect jz .disconnect
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ConnectedTime+ecx*4], eax mov [esi+usb_controller.ConnectedTime+ecx*4], eax
bts [esi+usb_controller.NewConnected], ecx bts [esi+usb_controller.NewConnected], ecx
jmp .nextport jmp .nextport
@ -1435,7 +1498,7 @@ proc ehci_process_deferred
.skip_roothub: .skip_roothub:
; 4. Process disconnect events. This should be done after step 3 ; 4. Process disconnect events. This should be done after step 3
; (which includes the first stage of disconnect processing). ; (which includes the first stage of disconnect processing).
call usb_disconnect_stage2 invoke usbhc_api.usb_disconnect_stage2
; 5. Check for previously connected devices. ; 5. Check for previously connected devices.
; If there is a connected device which was connected less than ; If there is a connected device which was connected less than
; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over. ; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over.
@ -1447,7 +1510,7 @@ proc ehci_process_deferred
.portloop2: .portloop2:
bt [esi+usb_controller.NewConnected], ecx bt [esi+usb_controller.NewConnected], ecx
jnc .noconnect jnc .noconnect
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+usb_controller.ConnectedTime+ecx*4] sub eax, [esi+usb_controller.ConnectedTime+ecx*4]
sub eax, USB_CONNECT_DELAY sub eax, USB_CONNECT_DELAY
jge .connected jge .connected
@ -1488,7 +1551,7 @@ proc ehci_process_deferred
; Satisfy a request when InterruptOnAsyncAdvance fired. ; Satisfy a request when InterruptOnAsyncAdvance fired.
test byte [esp+4], 20h test byte [esp+4], 20h
jz @f jz @f
dbgstr 'async advance int' ; dbgstr 'async advance int'
mov eax, [esi+usb_controller.WaitPipeRequestAsync] mov eax, [esi+usb_controller.WaitPipeRequestAsync]
mov [esi+usb_controller.ReadyPipeHeadAsync], eax mov [esi+usb_controller.ReadyPipeHeadAsync], eax
@@: @@:
@ -1531,7 +1594,7 @@ proc ehci_process_deferred
cmp [esi+usb_controller.ResettingStatus], 1 cmp [esi+usb_controller.ResettingStatus], 1
jnz .no_reset_in_progress jnz .no_reset_in_progress
; 8b. Yep. Test whether it should be stopped. ; 8b. Yep. Test whether it should be stopped.
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+usb_controller.ResetTime] sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_TIME sub eax, USB_RESET_TIME
jge .reset_done jge .reset_done
@ -1549,7 +1612,7 @@ proc ehci_process_deferred
cmp [esi+usb_controller.ResettingStatus], 0 cmp [esi+usb_controller.ResettingStatus], 0
jz .skip_reset jz .skip_reset
; 8f. Yep. Test whether it should be stopped. ; 8f. Yep. Test whether it should be stopped.
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+usb_controller.ResetTime] sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME sub eax, USB_RESET_RECOVERY_TIME
jge .reset_recovery_done jge .reset_recovery_done
@ -1568,7 +1631,7 @@ proc ehci_process_deferred
; 9. Process wait-done notifications, test for new wait requests. ; 9. Process wait-done notifications, test for new wait requests.
; Note: that must be done after steps 4 and 7 which could create new requests. ; Note: that must be done after steps 4 and 7 which could create new requests.
; 9a. Call the worker function. ; 9a. Call the worker function.
call usb_process_wait_lists invoke usbhc_api.usb_process_wait_lists
; 9b. If it reports that an asynchronous endpoint should be removed, ; 9b. If it reports that an asynchronous endpoint should be removed,
; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s ; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s
; (sometimes it just does not fire). ; (sometimes it just does not fire).
@ -1577,7 +1640,7 @@ proc ehci_process_deferred
mov edx, [esi+usb_controller.WaitPipeListAsync] mov edx, [esi+usb_controller.WaitPipeListAsync]
mov [esi+usb_controller.WaitPipeRequestAsync], edx mov [esi+usb_controller.WaitPipeRequestAsync], edx
or dword [edi+EhciCommandReg], 1 shl 6 or dword [edi+EhciCommandReg], 1 shl 6
dbgstr 'async advance doorbell' ; dbgstr 'async advance doorbell'
cmp dword [esp], 100 cmp dword [esp], 100
jb @f jb @f
mov dword [esp], 100 mov dword [esp], 100
@ -1649,7 +1712,7 @@ proc ehci_process_updated_list
; of the queue until a) the last descriptor (not the part of the queue itself) ; of the queue until a) the last descriptor (not the part of the queue itself)
; or b) an active (not yet processed by the hardware) descriptor is reached. ; or b) an active (not yet processed by the hardware) descriptor is reached.
lea ecx, [ebx+usb_pipe.Lock] lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock invoke MutexLock
mov ebx, [ebx+usb_pipe.LastTD] mov ebx, [ebx+usb_pipe.LastTD]
push ebx push ebx
mov ebx, [ebx+usb_gtd.NextVirt] mov ebx, [ebx+usb_gtd.NextVirt]
@ -1663,13 +1726,13 @@ proc ehci_process_updated_list
; Release the queue lock while processing one descriptor: ; Release the queue lock while processing one descriptor:
; callback function could (and often would) schedule another transfer. ; callback function could (and often would) schedule another transfer.
push ecx push ecx
call mutex_unlock invoke MutexUnlock
call ehci_process_updated_td call ehci_process_updated_td
pop ecx pop ecx
call mutex_lock invoke MutexLock
jmp .tdloop jmp .tdloop
.tddone: .tddone:
call mutex_unlock invoke MutexUnlock
pop ebx pop ebx
; End of internal loop, restore pointer to the next pipe ; End of internal loop, restore pointer to the next pipe
; and continue the external loop. ; and continue the external loop.
@ -1695,7 +1758,7 @@ proc ehci_process_updated_td
; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28] ; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28]
;@@: ;@@:
; 1. Remove this descriptor from the list of descriptors for this pipe. ; 1. Remove this descriptor from the list of descriptors for this pipe.
call usb_unlink_td invoke usbhc_api.usb_unlink_td
; 2. Calculate actual number of bytes transferred. ; 2. Calculate actual number of bytes transferred.
mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd]
lea edx, [eax+eax] lea edx, [eax+eax]
@ -1723,21 +1786,7 @@ proc ehci_process_updated_td
; 4. Either the descriptor in ebx was processed without errors, ; 4. Either the descriptor in ebx was processed without errors,
; or all necessary error actions were taken and ebx points to the last ; or all necessary error actions were taken and ebx points to the last
; related descriptor. ; related descriptor.
; 4a. Test whether it is the last descriptor in the transfer invoke usbhc_api.usb_process_gtd
; <=> it has an associated callback.
mov eax, [ebx+usb_gtd.Callback]
test eax, eax
jz .nocallback
; 4b. It has an associated callback; call it with corresponding parameters.
stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
jmp .callback
.nocallback:
; 4c. It is an intermediate descriptor. Add its length to the length
; in the following descriptor.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
.callback:
; 5. Free the current descriptor and return the next one. ; 5. Free the current descriptor and return the next one.
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
stdcall ehci_free_td, ebx stdcall ehci_free_td, ebx
@ -1765,12 +1814,12 @@ proc ehci_process_updated_td
; for this transfer. Free and unlink non-final descriptors. ; for this transfer. Free and unlink non-final descriptors.
; Final descriptor will be freed in step 5. ; Final descriptor will be freed in step 5.
.look_final: .look_final:
call usb_is_final_packet invoke usbhc_api.usb_is_final_packet
jnc .found_final jnc .found_final
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
stdcall ehci_free_td, ebx stdcall ehci_free_td, ebx
pop ebx pop ebx
call usb_unlink_td invoke usbhc_api.usb_unlink_td
jmp .look_final jmp .look_final
.found_final: .found_final:
; 6c. Restore the status saved in 6a and transform it to the error code. ; 6c. Restore the status saved in 6a and transform it to the error code.
@ -1822,7 +1871,7 @@ proc ehci_process_updated_td
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
stdcall ehci_free_td, ebx stdcall ehci_free_td, ebx
pop ebx pop ebx
call usb_unlink_td invoke usbhc_api.usb_unlink_td
pop ecx pop ecx
.normal: .normal:
; 6f. For bulk/interrupt transfers we have no choice but halt the queue, ; 6f. For bulk/interrupt transfers we have no choice but halt the queue,
@ -1882,7 +1931,7 @@ endp
proc ehci_alloc_td proc ehci_alloc_td
push ebx push ebx
mov ebx, ehci_gtd_mutex mov ebx, ehci_gtd_mutex
stdcall usb_allocate_common, (sizeof.ehci_gtd + sizeof.usb_gtd + 1Fh) and not 1Fh invoke usbhc_api.usb_allocate_common, (sizeof.ehci_gtd + sizeof.usb_gtd + 1Fh) and not 1Fh
test eax, eax test eax, eax
jz @f jz @f
add eax, sizeof.ehci_gtd add eax, sizeof.ehci_gtd
@ -1896,5 +1945,15 @@ endp
; EHCI has no additional data, so just free ehci_gtd structure. ; EHCI has no additional data, so just free ehci_gtd structure.
proc ehci_free_td proc ehci_free_td
sub dword [esp+4], sizeof.ehci_gtd sub dword [esp+4], sizeof.ehci_gtd
jmp usb_free_common jmp [usbhc_api.usb_free_common]
endp endp
include 'ehci_scheduler.inc'
section '.data' readable writable
include '../peimport.inc'
include_debug_strings
IncludeIGlobals
IncludeUGlobals
align 4
usbhc_api usbhc_func

View File

@ -2,233 +2,6 @@
; Bandwidth dedicated to periodic transactions is limited, so ; Bandwidth dedicated to periodic transactions is limited, so
; different pipes should be scheduled as uniformly as possible. ; different pipes should be scheduled as uniformly as possible.
; USB1 scheduler.
; Algorithm is simple:
; when adding a pipe, optimize the following quantity:
; * for every millisecond, take all bandwidth scheduled to periodic transfers,
; * calculate maximum over all milliseconds,
; * select a variant which minimizes that maximum;
; when removing a pipe, do nothing (except for bookkeeping).
; sanity check: structures in UHCI and OHCI should be the same
if (sizeof.ohci_static_ep=sizeof.uhci_static_ep)&(ohci_static_ep.SoftwarePart=uhci_static_ep.SoftwarePart)&(ohci_static_ep.NextList=uhci_static_ep.NextList)
; Select a list for a new pipe.
; in: esi -> usb_controller, maxpacket, type, interval can be found in the stack
; in: ecx = 2 * maximal interval = total number of periodic lists + 1
; in: edx -> {u|o}hci_static_ep for the first list
; in: eax -> byte past {u|o}hci_static_ep for the last list in the first group
; out: edx -> usb_static_ep for the selected list or zero if failed
proc usb1_select_interrupt_list
; inherit some variables from usb_open_pipe
virtual at ebp-12
.speed db ?
rb 3
.bandwidth dd ?
.target dd ?
dd ?
dd ?
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
push ebx edi ; save used registers to be stdcall
push eax ; save eax for checks in step 3
; 1. Only intervals 2^k ms can be supported.
; The core specification says that the real interval should not be greater
; than the interval given by the endpoint descriptor, but can be less.
; Determine the actual interval as 2^k ms.
mov eax, ecx
; 1a. Set [.interval] to 1 if it was zero; leave it as is otherwise
cmp [.interval], 1
adc [.interval], 0
; 1b. Divide ecx by two while it is strictly greater than [.interval].
@@:
shr ecx, 1
cmp [.interval], ecx
jb @b
; ecx = the actual interval
;
; For example, let ecx = 8, eax = 64.
; The scheduler space is 32 milliseconds,
; we need to schedule something every 8 ms;
; there are 8 variants: schedule at times 0,8,16,24,
; schedule at times 1,9,17,25,..., schedule at times 7,15,23,31.
; Now concentrate: there are three nested loops,
; * the innermost loop calculates the total periodic bandwidth scheduled
; in the given millisecond,
; * the intermediate loop calculates the maximum over all milliseconds
; in the given variant, that is the quantity we're trying to minimize,
; * the outermost loop checks all variants.
; 2. Calculate offset between the first list and the first list for the
; selected interval, in bytes; save in the stack for step 4.
sub eax, ecx
sub eax, ecx
imul eax, sizeof.ohci_static_ep
push eax
imul ebx, ecx, sizeof.ohci_static_ep
; 3. Select the best variant.
; 3a. The outermost loop.
; Prepare for the loop: set the current optimal bandwidth to maximum
; possible value (so that any variant will pass the first comparison),
; calculate delta for the intermediate loop.
or [.bandwidth], -1
.varloop:
; 3b. The intermediate loop.
; Prepare for the loop: set the maximum to be calculated to zero,
; save counter of the outermost loop.
xor edi, edi
push edx
virtual at esp
.cur_variant dd ? ; step 3b
.result_delta dd ? ; step 2
.group1_limit dd ? ; function prolog
end virtual
.calc_max_bandwidth:
; 3c. The innermost loop. Sum over all lists.
xor eax, eax
push edx
.calc_bandwidth:
add eax, [edx+ohci_static_ep.SoftwarePart+usb_static_ep.Bandwidth]
mov edx, [edx+ohci_static_ep.NextList]
test edx, edx
jnz .calc_bandwidth
pop edx
; 3d. The intermediate loop continued: update maximum.
cmp eax, edi
jb @f
mov edi, eax
@@:
; 3e. The intermediate loop continued: advance counter.
add edx, ebx
cmp edx, [.group1_limit]
jb .calc_max_bandwidth
; 3e. The intermediate loop done: restore counter of the outermost loop.
pop edx
; 3f. The outermost loop continued: if the current variant is
; better (maybe not strictly) then the previous optimum, update
; the optimal bandwidth and resulting list.
cmp edi, [.bandwidth]
ja @f
mov [.bandwidth], edi
mov [.target], edx
@@:
; 3g. The outermost loop continued: advance counter.
add edx, sizeof.ohci_static_ep
dec ecx
jnz .varloop
; 4. Calculate bandwidth for the new pipe.
mov eax, [.maxpacket]
mov cl, [.speed]
mov ch, byte [.endpoint]
and ch, 80h
call calc_usb1_bandwidth
; 5. Get the pointer to the best list.
pop edx ; restore value from step 2
pop ecx ; purge stack var from prolog
add edx, [.target]
; 6. Check that bandwidth for the new pipe plus old bandwidth
; still fits to maximum allowed by the core specification, 90% of 12000 bits.
mov ecx, eax
add ecx, [.bandwidth]
cmp ecx, 10800
ja .no_bandwidth
; 7. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return.
add edx, ohci_static_ep.SoftwarePart
add [edx+usb_static_ep.Bandwidth], eax
pop edi ebx ; restore used registers to be stdcall
ret
.no_bandwidth:
dbgstr 'Periodic bandwidth limit reached'
xor edx, edx
pop edi ebx
ret
endp
; sanity check, part 2
else
.err select_interrupt_list must be different for UHCI and OHCI
end if
; Pipe is removing, update the corresponding lists.
; We do not reorder anything, so just update book-keeping variable
; in the list header.
proc usb1_interrupt_list_unlink
virtual at esp
dd ? ; return address
.maxpacket dd ?
.lowspeed db ?
.direction db ?
rb 2
end virtual
; calculate bandwidth on the bus
mov eax, [.maxpacket]
mov ecx, dword [.lowspeed]
call calc_usb1_bandwidth
; find list header
mov edx, ebx
@@:
mov edx, [edx+usb_pipe.NextVirt]
cmp [edx+usb_pipe.Controller], esi
jz @b
; subtract pipe bandwidth
sub [edx+usb_static_ep.Bandwidth], eax
ret 8
endp
; Helper procedure for USB1 scheduler: calculate bandwidth on the bus.
; in: low 11 bits of eax = payload size in bytes
; in: cl = 0 - full-speed, nonzero - high-speed
; in: ch = 0 - OUT, nonzero - IN
; out: eax = maximal bandwidth in FS-bits
proc calc_usb1_bandwidth
and eax, (1 shl 11) - 1 ; get payload for one transaction
add eax, 3 ; add 3 bytes for other fields in data packet, PID+CRC16
test cl, cl
jnz .low_speed
; Multiply by 8 for bytes -> bits, by 7/6 to accomodate bit stuffing
; and by 401/400 for IN transfers to accomodate timers difference
; 9+107/300 for IN transfers, 9+1/3 for OUT transfers
; For 0 <= eax < 09249355h, floor(eax * 107/300) = floor(eax * 5B4E81B5h / 2^32).
; For 0 <= eax < 80000000h, floor(eax / 3) = floor(eax * 55555556h / 2^32).
mov edx, 55555556h
test ch, ch
jz @f
mov edx, 5B4E81B5h
@@:
lea ecx, [eax*9]
mul edx
; Add 93 extra bits: 39 bits for Token packet (8 for SYNC, 24 for token+address,
; 4 extra bits for possible bit stuffing in token+address, 3 for EOP),
; 18 bits for bus turn-around, 11 bits for SYNC+EOP in Data packet plus 1 bit
; for possible timers difference, 2 bits for inter-packet delay, 20 bits for
; Handshake packet, 2 bits for another inter-packet delay.
lea eax, [ecx+edx+93]
ret
.low_speed:
; Multiply by 8 for bytes -> bits, by 7/6 to accomodate bit stuffing,
; by 8 for LS -> FS and by 406/50 for IN transfers to accomodate timers difference.
; 75+59/75 for IN transfers, 74+2/3 for OUT transfers.
mov edx, 0AAAAAABh
test ch, ch
mov ecx, 74
jz @f
mov edx, 0C962FC97h
inc ecx
@@:
imul ecx, eax
mul edx
; Add 778 extra bits:
; 16 bits for PRE packet, 4 bits for hub delay, 8*39 bits for Token packet
; 8*18 bits for bus turn-around
; (406/50)*11 bits for SYNC+EOP in Data packet,
; 8*2 bits for inter-packet delay,
; 16 bits for PRE packet, 4 bits for hub delay, 8*20 bits for Handshake packet,
; 8*2 bits for another inter-packet delay.
lea eax, [ecx+edx+778]
ret
endp
; USB2 scheduler. ; USB2 scheduler.
; There are two parts: high-speed pipes and split-transaction pipes. ; There are two parts: high-speed pipes and split-transaction pipes.
; ;
@ -830,6 +603,9 @@ virtual at ebp-12-.local_vars_size
.variant_delta dd ? .variant_delta dd ?
.target_delta dd ? .target_delta dd ?
.local_vars_size = $ - .local_vars_start .local_vars_size = $ - .local_vars_start
if .local_vars_size > 24*4
err Modify stack frame size in
end if
.targetsmask dd ? .targetsmask dd ?
.bandwidth dd ? .bandwidth dd ?
@ -992,9 +768,7 @@ endp
; Similar to calc_usb1_bandwidth with corresponding changes. ; Similar to calc_usb1_bandwidth with corresponding changes.
; eax -> usb_hub with TT, ebx -> usb_pipe ; eax -> usb_hub with TT, ebx -> usb_pipe
proc tt_calc_budget proc tt_calc_budget
movzx ecx, [eax+usb_hub.HubCharacteristics] invoke usbhc_api.usb_get_tt_think_time ; ecx = TT think time in FS-bytes
shr ecx, 5
and ecx, 3 ; 1+ecx = TT think time in FS-bytes
mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe]
shr eax, 16 shr eax, 16
and eax, (1 shl 11) - 1 ; get data length and eax, (1 shl 11) - 1 ; get data length
@ -1005,7 +779,7 @@ proc tt_calc_budget
; 18 bits for bus turn-around, 11 bits for SYNC+EOP in Data packet, ; 18 bits for bus turn-around, 11 bits for SYNC+EOP in Data packet,
; 2 bits for inter-packet delay, 19 bits for Handshake packet, ; 2 bits for inter-packet delay, 19 bits for Handshake packet,
; 2 bits for another inter-packet delay. 85 bits total, pad to 11 bytes. ; 2 bits for another inter-packet delay. 85 bits total, pad to 11 bytes.
lea eax, [eax+11+ecx+1] lea eax, [eax+11+ecx]
; 1 byte is minimal TT think time in addition to ecx. ; 1 byte is minimal TT think time in addition to ecx.
ret ret
.low_speed: .low_speed:
@ -1014,7 +788,7 @@ proc tt_calc_budget
; add 85 bytes as in full-speed interrupt and extra 5 bytes for two PRE packets ; add 85 bytes as in full-speed interrupt and extra 5 bytes for two PRE packets
; and two hub delays. ; and two hub delays.
; 1 byte is minimal TT think time in addition to ecx. ; 1 byte is minimal TT think time in addition to ecx.
lea eax, [eax*8+90+ecx+1] lea eax, [eax*8+90+ecx]
ret ret
endp endp

View File

@ -1,7 +1,17 @@
; Code for OHCI controllers. ; Code for OHCI controllers.
; Note: it should be moved to an external driver,
; it was convenient to have this code compiled into the kernel during initial ; Standard driver stuff
; development, but there are no reasons to keep it here. format PE DLL native
entry start
__DEBUG__ equ 1
__DEBUG_LEVEL__ equ 1
section '.reloc' data readable discardable fixups
section '.text' code readable executable
include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../../kernel/trunk/bus/usb/common.inc'
; ============================================================================= ; =============================================================================
; ================================= Constants ================================= ; ================================= Constants =================================
@ -183,6 +193,8 @@ DoneListEndPtr dd ?
; Pointer to dword which should receive a pointer to the next item in DoneList. ; Pointer to dword which should receive a pointer to the next item in DoneList.
; If DoneList is empty, this is a pointer to DoneList itself; ; If DoneList is empty, this is a pointer to DoneList itself;
; otherwise, this is a pointer to NextTD field of the last item in DoneList. ; otherwise, this is a pointer to NextTD field of the last item in DoneList.
EhciCompanion dd ?
; Pointer to usb_controller for EHCI companion, if any, or NULL.
ends ends
if ohci_controller.IntEDs mod 16 if ohci_controller.IntEDs mod 16
@ -290,8 +302,10 @@ ends
iglobal iglobal
align 4 align 4
ohci_hardware_func: ohci_hardware_func:
dd USBHC_VERSION
dd 'OHCI' dd 'OHCI'
dd sizeof.ohci_controller dd sizeof.ohci_controller
dd ohci_kickoff_bios
dd ohci_init dd ohci_init
dd ohci_process_deferred dd ohci_process_deferred
dd ohci_set_device_address dd ohci_set_device_address
@ -299,21 +313,50 @@ ohci_hardware_func:
dd ohci_port_disable dd ohci_port_disable
dd ohci_new_port.reset dd ohci_new_port.reset
dd ohci_set_endpoint_packet_size dd ohci_set_endpoint_packet_size
dd usb1_allocate_endpoint dd ohci_alloc_pipe
dd usb1_free_endpoint dd ohci_free_pipe
dd ohci_init_pipe dd ohci_init_pipe
dd ohci_unlink_pipe dd ohci_unlink_pipe
dd usb1_allocate_general_td dd ohci_alloc_gtd
dd usb1_free_general_td dd ohci_free_gtd
dd ohci_alloc_transfer dd ohci_alloc_transfer
dd ohci_insert_transfer dd ohci_insert_transfer
dd ohci_new_device dd ohci_new_device
ohci_name db 'OHCI',0
endg endg
; ============================================================================= ; =============================================================================
; =================================== Code ==================================== ; =================================== Code ====================================
; ============================================================================= ; =============================================================================
; Called once when driver is loading and once at shutdown.
; When loading, must initialize itself, register itself in the system
; and return eax = value obtained when registering.
proc start
virtual at esp
dd ? ; return address
.reason dd ? ; DRV_ENTRY or DRV_EXIT
.cmdline dd ? ; normally NULL
end virtual
cmp [.reason], DRV_ENTRY
jnz .nothing
mov ecx, ohci_ep_mutex
and dword [ecx-4], 0
invoke MutexInit
mov ecx, ohci_gtd_mutex
and dword [ecx-4], 0
invoke MutexInit
push esi edi
mov esi, [USBHCFunc]
mov edi, usbhc_api
movi ecx, sizeof.usbhc_func/4
rep movsd
pop edi esi
invoke RegUSBDriver, ohci_name, 0, ohci_hardware_func
.nothing:
ret
endp
; Controller-specific initialization function. ; Controller-specific initialization function.
; Called from usb_init_controller. Initializes the hardware and ; Called from usb_init_controller. Initializes the hardware and
; OHCI-specific parts of software structures. ; OHCI-specific parts of software structures.
@ -338,7 +381,7 @@ if ohci_controller.IntEDs >= 0x1000
.err assertion failed .err assertion failed
end if end if
lea esi, [eax+ohci_controller.IntEDs+32*sizeof.ohci_static_ep] lea esi, [eax+ohci_controller.IntEDs+32*sizeof.ohci_static_ep]
call get_pg_addr invoke GetPgAddr
add eax, ohci_controller.IntEDs add eax, ohci_controller.IntEDs
movi ecx, 32 movi ecx, 32
mov edx, ecx mov edx, ecx
@ -383,23 +426,14 @@ end if
call ohci_init_static_endpoint call ohci_init_static_endpoint
; 4. Create a virtual memory area to talk with the controller. ; 4. Create a virtual memory area to talk with the controller.
; 4a. Enable memory & bus master access. ; 4a. Enable memory & bus master access.
mov ch, [.bus] invoke PciRead16, dword [.bus], dword [.devfn], 4
mov cl, 0
mov eax, ecx
mov bh, [.devfn]
mov bl, 4
call pci_read_reg
or al, 6 or al, 6
xchg eax, ecx invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax
call pci_write_reg
; 4b. Read memory base address. ; 4b. Read memory base address.
mov ah, [.bus] invoke PciRead32, dword [.bus], dword [.devfn], 10h
mov al, 2
mov bl, 10h
call pci_read_reg
and al, not 0Fh and al, not 0Fh
; 4c. Create mapping for physical memory. 256 bytes are sufficient. ; 4c. Create mapping for physical memory. 256 bytes are sufficient.
stdcall map_io_mem, eax, 100h, PG_SW+PG_NOCACHE invoke MapIoMem, eax, 100h, PG_SW+PG_NOCACHE
test eax, eax test eax, eax
jz .fail jz .fail
stosd ; fill ohci_controller.MMIOBase stosd ; fill ohci_controller.MMIOBase
@ -422,7 +456,7 @@ end if
mov [edi+OhciCommandStatusReg], ecx mov [edi+OhciCommandStatusReg], ecx
@@: @@:
mov esi, ecx mov esi, ecx
call delay_ms invoke Sleep
test [edi+OhciCommandStatusReg], ecx test [edi+OhciCommandStatusReg], ecx
jz .resetdone jz .resetdone
dec edx dec edx
@ -454,7 +488,7 @@ end if
pop esi ; restore pointer to ohci_controller saved in step 1 pop esi ; restore pointer to ohci_controller saved in step 1
; 6a. Physical address of HCCA. ; 6a. Physical address of HCCA.
mov eax, esi mov eax, esi
call get_pg_addr invoke GetPgAddr
mov [edi+OhciHCCAReg], eax mov [edi+OhciHCCAReg], eax
; 6b. Transition to operational state and clear all Enable bits. ; 6b. Transition to operational state and clear all Enable bits.
mov cl, 2 shl 6 mov cl, 2 shl 6
@ -475,28 +509,29 @@ end if
; mov [edi+OhciDoneHeadReg], eax ; mov [edi+OhciDoneHeadReg], eax
; 6e. Enable processing of all lists with control:bulk ratio = 1:1. ; 6e. Enable processing of all lists with control:bulk ratio = 1:1.
mov dword [edi+OhciControlReg], 10111100b mov dword [edi+OhciControlReg], 10111100b
; 7. Get number of ports. ; 7. Find the EHCI companion.
; Note: this assumes that EHCI is initialized before USB1 companions.
add esi, sizeof.ohci_controller add esi, sizeof.ohci_controller
mov ebx, dword [.devfn]
invoke usbhc_api.usb_find_ehci_companion
mov [esi+ohci_controller.EhciCompanion-sizeof.ohci_controller], eax
; 8. Get number of ports.
mov eax, [edi+OhciRhDescriptorAReg] mov eax, [edi+OhciRhDescriptorAReg]
and eax, 0xF and eax, 0xF
mov [esi+usb_controller.NumPorts], eax mov [esi+usb_controller.NumPorts], eax
; 8. Initialize DoneListEndPtr to point to DoneList. ; 9. Initialize DoneListEndPtr to point to DoneList.
lea eax, [esi+ohci_controller.DoneList-sizeof.ohci_controller] lea eax, [esi+ohci_controller.DoneList-sizeof.ohci_controller]
mov [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], eax mov [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], eax
; 9. Hook interrupt. ; 10. Hook interrupt.
mov ah, [.bus] invoke PciRead8, dword [.bus], dword [.devfn], 3Ch
mov al, 0
mov bh, [.devfn]
mov bl, 3Ch
call pci_read_reg
; al = IRQ ; al = IRQ
movzx eax, al movzx eax, al
stdcall attach_int_handler, eax, ohci_irq, esi invoke AttachIntHandler, eax, ohci_irq, esi
; 10. Enable controller interrupt on HcDoneHead writeback and RootHubStatusChange. ; 11. Enable controller interrupt on HcDoneHead writeback and RootHubStatusChange.
mov dword [edi+OhciInterruptEnableReg], 80000042h mov dword [edi+OhciInterruptEnableReg], 80000042h
DEBUGF 1,'K : OHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] DEBUGF 1,'K : OHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts]
; 11. Initialize ports of the controller. ; 12. Initialize ports of the controller.
; 11a. Initiate power up, disable all ports, clear all "changed" bits. ; 12a. Initiate power up, disable all ports, clear all "changed" bits.
mov dword [edi+OhciRhStatusReg], 10000h ; SetGlobalPower mov dword [edi+OhciRhStatusReg], 10000h ; SetGlobalPower
xor ecx, ecx xor ecx, ecx
@@: @@:
@ -504,20 +539,20 @@ end if
inc ecx inc ecx
cmp ecx, [esi+usb_controller.NumPorts] cmp ecx, [esi+usb_controller.NumPorts]
jb @b jb @b
; 11b. Wait for power up. ; 12b. Wait for power up.
; VirtualBox has AReg == 0, delay_ms doesn't like zero value; ignore zero delay ; VirtualBox has AReg == 0, delay_ms doesn't like zero value; ignore zero delay
push esi push esi
mov esi, [edi+OhciRhDescriptorAReg] mov esi, [edi+OhciRhDescriptorAReg]
shr esi, 24 shr esi, 24
add esi, esi add esi, esi
jz @f jz @f
call delay_ms invoke Sleep
@@: @@:
pop esi pop esi
; 11c. Ports are powered up; now it is ok to process connect/disconnect events. ; 12c. Ports are powered up; now it is ok to process connect/disconnect events.
mov [esi+ohci_controller.PoweredUp-sizeof.ohci_controller], 1 mov [esi+ohci_controller.PoweredUp-sizeof.ohci_controller], 1
; IRQ handler doesn't accept connect/disconnect events before this point ; IRQ handler doesn't accept connect/disconnect events before this point
; 11d. We could miss some events while waiting for powering up; ; 12d. We could miss some events while waiting for powering up;
; scan all ports manually and check for connected devices. ; scan all ports manually and check for connected devices.
xor ecx, ecx xor ecx, ecx
.port_loop: .port_loop:
@ -527,23 +562,23 @@ end if
; and save the connected time. ; and save the connected time.
; Note that ConnectedTime must be set before 'connected' mark, ; Note that ConnectedTime must be set before 'connected' mark,
; otherwise the code in ohci_process_deferred could use incorrect time. ; otherwise the code in ohci_process_deferred could use incorrect time.
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ConnectedTime+ecx*4], eax mov [esi+usb_controller.ConnectedTime+ecx*4], eax
lock bts [esi+usb_controller.NewConnected], ecx lock bts [esi+usb_controller.NewConnected], ecx
.next_port: .next_port:
inc ecx inc ecx
cmp ecx, [esi+usb_controller.NumPorts] cmp ecx, [esi+usb_controller.NumPorts]
jb .port_loop jb .port_loop
; 12. Return pointer to usb_controller. ; 13. Return pointer to usb_controller.
xchg eax, esi xchg eax, esi
ret ret
.fail_unmap: .fail_unmap:
; On error after step 4, release the virtual memory area. ; On error after step 5, release the virtual memory area.
stdcall free_kernel_space, edi invoke FreeKernelSpace, edi
.fail: .fail:
; On error, free the ohci_controller structure and return zero. ; On error, free the ohci_controller structure and return zero.
; Note that the pointer was placed in the stack at step 1. ; Note that the pointer was placed in the stack at step 1.
; Note also that there can be no errors after step 8, ; Note also that there can be no errors after step 6,
; where that pointer is popped from the stack. ; where that pointer is popped from the stack.
pop ecx pop ecx
.nothing: .nothing:
@ -561,7 +596,7 @@ proc ohci_init_static_endpoint
mov [edi+ohci_static_ep.NextED], eax mov [edi+ohci_static_ep.NextED], eax
mov [edi+ohci_static_ep.NextList], esi mov [edi+ohci_static_ep.NextList], esi
add edi, ohci_static_ep.SoftwarePart add edi, ohci_static_ep.SoftwarePart
call usb_init_static_endpoint invoke usbhc_api.usb_init_static_endpoint
add edi, sizeof.ohci_static_ep - ohci_static_ep.SoftwarePart add edi, sizeof.ohci_static_ep - ohci_static_ep.SoftwarePart
ret ret
endp endp
@ -591,14 +626,10 @@ endp
; deal with USB devices. ; deal with USB devices.
proc ohci_kickoff_bios proc ohci_kickoff_bios
; 1. Get the physical address of MMIO registers. ; 1. Get the physical address of MMIO registers.
mov ah, [esi+PCIDEV.bus] invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 10h
mov bh, [esi+PCIDEV.devfn]
mov al, 2
mov bl, 10h
call pci_read_reg
and al, not 0Fh and al, not 0Fh
; 2. Create mapping for physical memory. 256 bytes are sufficient. ; 2. Create mapping for physical memory. 256 bytes are sufficient.
stdcall map_io_mem, eax, 100h, PG_SW+PG_NOCACHE invoke MapIoMem, eax, 100h, PG_SW+PG_NOCACHE
test eax, eax test eax, eax
jz .nothing jz .nothing
; 3. Some BIOSes enable controller interrupts as a result of giving ; 3. Some BIOSes enable controller interrupts as a result of giving
@ -625,7 +656,7 @@ proc ohci_kickoff_bios
jz .has_ownership jz .has_ownership
push esi push esi
movi esi, 1 movi esi, 1
call delay_ms invoke Sleep
pop esi pop esi
loop @b loop @b
dbgstr 'warning: taking OHCI ownership from BIOS timeout' dbgstr 'warning: taking OHCI ownership from BIOS timeout'
@ -636,7 +667,7 @@ proc ohci_kickoff_bios
; 6. Now we can unblock interrupts in the processor. ; 6. Now we can unblock interrupts in the processor.
popf popf
; 7. Release memory mapping created in step 2 and return. ; 7. Release memory mapping created in step 2 and return.
stdcall free_kernel_space, eax invoke FreeKernelSpace, eax
.nothing: .nothing:
ret ret
endp endp
@ -703,7 +734,8 @@ end virtual
xor ebx, ebx xor ebx, ebx
test ecx, ecx test ecx, ecx
jz .tddone jz .tddone
call usb_td_to_virt mov eax, [ohci_gtd_first_page]
invoke usbhc_api.usb_td_to_virt
test eax, eax test eax, eax
jz .tddone jz .tddone
lea edx, [eax+ohci_gtd.NextTD] lea edx, [eax+ohci_gtd.NextTD]
@ -718,7 +750,8 @@ end virtual
lea ebx, [eax+sizeof.ohci_gtd] lea ebx, [eax+sizeof.ohci_gtd]
test ecx, ecx test ecx, ecx
jz .tddone jz .tddone
call usb_td_to_virt mov eax, [ohci_gtd_first_page]
invoke usbhc_api.usb_td_to_virt
test eax, eax test eax, eax
jnz .tdloop jnz .tdloop
.tddone: .tddone:
@ -845,7 +878,7 @@ end virtual
jz .disconnect jz .disconnect
; Note: ConnectedTime must be stored before setting the 'connected' bit, ; Note: ConnectedTime must be stored before setting the 'connected' bit,
; otherwise ohci_process_deferred could use an old time. ; otherwise ohci_process_deferred could use an old time.
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ConnectedTime+ecx*4], eax mov [esi+usb_controller.ConnectedTime+ecx*4], eax
lock bts [esi+usb_controller.NewConnected], ecx lock bts [esi+usb_controller.NewConnected], ecx
jmp .nextport jmp .nextport
@ -858,8 +891,8 @@ end virtual
jz .nextport jz .nextport
test al, 10h test al, 10h
jnz .nextport jnz .nextport
mov edx, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ResetTime], edx mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 2 mov [esi+usb_controller.ResettingStatus], 2
inc ebx inc ebx
.nextport: .nextport:
@ -871,7 +904,7 @@ end virtual
; 12. Restore the stack after step 6. ; 12. Restore the stack after step 6.
pop eax pop eax
; 13. Notify the USB thread if some deferred processing is required. ; 13. Notify the USB thread if some deferred processing is required.
call usb_wakeup_if_needed invoke usbhc_api.usb_wakeup_if_needed
; 14. Interrupt processed; return something non-zero. ; 14. Interrupt processed; return something non-zero.
mov al, 1 mov al, 1
pop edi esi ebx ; restore used registers to be stdcall pop edi esi ebx ; restore used registers to be stdcall
@ -884,8 +917,7 @@ endp
proc ohci_set_device_address proc ohci_set_device_address
mov byte [ebx+ohci_pipe.Flags-sizeof.ohci_pipe], cl mov byte [ebx+ohci_pipe.Flags-sizeof.ohci_pipe], cl
; Wait until the hardware will forget the old value. ; Wait until the hardware will forget the old value.
call usb_subscribe_control jmp [usbhc_api.usb_subscribe_control]
ret
endp endp
; This procedure returns USB device address from the usb_pipe structure. ; This procedure returns USB device address from the usb_pipe structure.
@ -914,8 +946,7 @@ endp
proc ohci_set_endpoint_packet_size proc ohci_set_endpoint_packet_size
mov byte [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe], cl mov byte [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe], cl
; Wait until the hardware will forget the old value. ; Wait until the hardware will forget the old value.
call usb_subscribe_control jmp [usbhc_api.usb_subscribe_control]
ret
endp endp
; This procedure is called from API usb_open_pipe and processes ; This procedure is called from API usb_open_pipe and processes
@ -938,7 +969,7 @@ virtual at ebp-12
end virtual end virtual
; 1. Initialize the queue of transfer descriptors: empty. ; 1. Initialize the queue of transfer descriptors: empty.
sub eax, sizeof.ohci_gtd sub eax, sizeof.ohci_gtd
call get_phys_addr invoke GetPhysAddr
mov [edi+ohci_pipe.TailP-sizeof.ohci_pipe], eax mov [edi+ohci_pipe.TailP-sizeof.ohci_pipe], eax
mov [edi+ohci_pipe.HeadP-sizeof.ohci_pipe], eax mov [edi+ohci_pipe.HeadP-sizeof.ohci_pipe], eax
; 2. Generate ohci_pipe.Flags, see the description in ohci_pipe. ; 2. Generate ohci_pipe.Flags, see the description in ohci_pipe.
@ -991,7 +1022,7 @@ end virtual
mov ecx, [edx+ohci_pipe.NextED-sizeof.ohci_pipe] mov ecx, [edx+ohci_pipe.NextED-sizeof.ohci_pipe]
mov [edi+ohci_pipe.NextED-sizeof.ohci_pipe], ecx mov [edi+ohci_pipe.NextED-sizeof.ohci_pipe], ecx
lea eax, [edi-sizeof.ohci_pipe] lea eax, [edi-sizeof.ohci_pipe]
call get_phys_addr invoke GetPhysAddr
mov [edx+ohci_pipe.NextED-sizeof.ohci_pipe], eax mov [edx+ohci_pipe.NextED-sizeof.ohci_pipe], eax
; 4. Return something non-zero. ; 4. Return something non-zero.
ret ret
@ -1114,7 +1145,7 @@ endl
.fail: .fail:
mov edi, ohci_hardware_func mov edi, ohci_hardware_func
mov eax, [td] mov eax, [td]
stdcall usb_undo_tds, [origTD] invoke usbhc_api.usb_undo_tds, [origTD]
xor eax, eax xor eax, eax
ret ret
endp endp
@ -1137,18 +1168,18 @@ virtual at ebp-8
.direction dd ? .direction dd ?
end virtual end virtual
; 1. Allocate the next TD. ; 1. Allocate the next TD.
call usb1_allocate_general_td call ohci_alloc_gtd
test eax, eax test eax, eax
jz .nothing jz .nothing
; 2. Initialize controller-independent parts of both TDs. ; 2. Initialize controller-independent parts of both TDs.
push eax push eax
call usb_init_transfer invoke usbhc_api.usb_init_transfer
pop eax pop eax
; 3. Save the returned value (next descriptor). ; 3. Save the returned value (next descriptor).
push eax push eax
; 4. Store the physical address of the next descriptor. ; 4. Store the physical address of the next descriptor.
sub eax, sizeof.ohci_gtd sub eax, sizeof.ohci_gtd
call get_phys_addr invoke GetPhysAddr
mov [ecx+ohci_gtd.NextTD-sizeof.ohci_gtd], eax mov [ecx+ohci_gtd.NextTD-sizeof.ohci_gtd], eax
; 5. For zero-length transfers, store zero in both fields for buffer addresses. ; 5. For zero-length transfers, store zero in both fields for buffer addresses.
; Otherwise, fill them with real values. ; Otherwise, fill them with real values.
@ -1158,12 +1189,12 @@ end virtual
cmp [.packetSize], eax cmp [.packetSize], eax
jz @f jz @f
mov eax, [.buffer] mov eax, [.buffer]
call get_phys_addr invoke GetPhysAddr
mov [ecx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd], eax mov [ecx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd], eax
mov eax, [.buffer] mov eax, [.buffer]
add eax, [.packetSize] add eax, [.packetSize]
dec eax dec eax
call get_phys_addr invoke GetPhysAddr
mov [ecx+ohci_gtd.BufEnd-sizeof.ohci_gtd], eax mov [ecx+ohci_gtd.BufEnd-sizeof.ohci_gtd], eax
@@: @@:
; 6. Generate Flags field: ; 6. Generate Flags field:
@ -1225,7 +1256,9 @@ proc ohci_port_after_reset
xor eax, eax xor eax, eax
xchg al, [esi+usb_controller.ResettingStatus] xchg al, [esi+usb_controller.ResettingStatus]
test al, al test al, al
js usb_test_pending_port jns @f
jmp [usbhc_api.usb_test_pending_port]
@@:
; If the controller has disabled the port (e.g. overcurrent), ; If the controller has disabled the port (e.g. overcurrent),
; continue to next device (if there is one). ; continue to next device (if there is one).
movzx ecx, [esi+usb_controller.ResettingPort] movzx ecx, [esi+usb_controller.ResettingPort]
@ -1233,13 +1266,13 @@ proc ohci_port_after_reset
test al, 2 test al, 2
jnz @f jnz @f
DEBUGF 1,'K : USB port disabled after reset, status=%x\n',eax DEBUGF 1,'K : USB port disabled after reset, status=%x\n',eax
jmp usb_test_pending_port jmp [usbhc_api.usb_test_pending_port]
@@: @@:
push ecx push ecx
; 2. Get LowSpeed bit to bit 0 of eax and call the worker procedure ; 2. Get LowSpeed bit to bit 0 of eax and call the worker procedure
; to notify the protocol layer about new OHCI device. ; to notify the protocol layer about new OHCI device.
mov eax, [edi+OhciRhPortStatusReg+ecx*4] mov eax, [edi+OhciRhPortStatusReg+ecx*4]
DEBUGF 1,'K : port_after_reset [%d] status of port %d is %x\n',[timer_ticks],ecx,eax DEBUGF 1,'K : port_after_reset, status of port %d is %x\n',ecx,eax
shr eax, 9 shr eax, 9
call ohci_new_device call ohci_new_device
pop ecx pop ecx
@ -1249,7 +1282,7 @@ proc ohci_port_after_reset
jnz .nothing jnz .nothing
.disable_exit: .disable_exit:
mov dword [edi+OhciRhPortStatusReg+ecx*4], 1 mov dword [edi+OhciRhPortStatusReg+ecx*4], 1
jmp usb_test_pending_port jmp [usbhc_api.usb_test_pending_port]
.nothing: .nothing:
ret ret
endp endp
@ -1272,7 +1305,7 @@ proc ohci_new_device
sub esp, 12 ; ignored fields sub esp, 12 ; ignored fields
push eax ; .Flags push eax ; .Flags
; 4. Notify the protocol layer. ; 4. Notify the protocol layer.
call usb_new_device invoke usbhc_api.usb_new_device
; 5. Cleanup the stack after step 3 and return. ; 5. Cleanup the stack after step 3 and return.
add esp, 20 add esp, 20
ret ret
@ -1287,7 +1320,7 @@ proc ohci_process_deferred
; 1. Initialize the return value. ; 1. Initialize the return value.
push -1 push -1
; 2. Process disconnect events. ; 2. Process disconnect events.
call usb_disconnect_stage2 invoke usbhc_api.usb_disconnect_stage2
; 3. Check for connected devices. ; 3. Check for connected devices.
; If there is a connected device which was connected less than ; If there is a connected device which was connected less than
; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over. ; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over.
@ -1299,7 +1332,15 @@ proc ohci_process_deferred
.portloop: .portloop:
bt [esi+usb_controller.NewConnected], ecx bt [esi+usb_controller.NewConnected], ecx
jnc .noconnect jnc .noconnect
mov eax, [timer_ticks] ; If this port is shared with the EHCI companion and we see the connect event,
; then the device is USB1 dropped by EHCI,
; so EHCI has already waited for debounce delay, we can proceed immediately.
cmp [esi+ohci_controller.EhciCompanion-sizeof.ohci_controller], 0
jz .portloop.test_time
dbgstr 'port is shared with EHCI, skipping initial debounce'
jmp .connected
.portloop.test_time:
invoke GetTimerTicks
sub eax, [esi+usb_controller.ConnectedTime+ecx*4] sub eax, [esi+usb_controller.ConnectedTime+ecx*4]
sub eax, USB_CONNECT_DELAY sub eax, USB_CONNECT_DELAY
jge .connected jge .connected
@ -1321,7 +1362,7 @@ proc ohci_process_deferred
; 4. Check for end of reset signalling. If so, call ohci_port_after_reset. ; 4. Check for end of reset signalling. If so, call ohci_port_after_reset.
cmp [esi+usb_controller.ResettingStatus], 2 cmp [esi+usb_controller.ResettingStatus], 2
jnz .no_reset_recovery jnz .no_reset_recovery
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+usb_controller.ResetTime] sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME sub eax, USB_RESET_RECOVERY_TIME
jge .reset_done jge .reset_done
@ -1353,7 +1394,7 @@ proc ohci_process_deferred
; 6. Process wait-done notifications, test for new wait requests. ; 6. Process wait-done notifications, test for new wait requests.
; Note: that must be done after steps 2 and 5 which could create new requests. ; Note: that must be done after steps 2 and 5 which could create new requests.
; 6a. Call the worker function from main USB code. ; 6a. Call the worker function from main USB code.
call usb_process_wait_lists invoke usbhc_api.usb_process_wait_lists
; 6b. If no new requests, skip the rest of this step. ; 6b. If no new requests, skip the rest of this step.
test eax, eax test eax, eax
jz @f jz @f
@ -1402,7 +1443,7 @@ proc ohci_process_finalized_td
jmp .next_td2 jmp .next_td2
@@: @@:
; 2. Remove the descriptor from the descriptors queue. ; 2. Remove the descriptor from the descriptors queue.
call usb_unlink_td invoke usbhc_api.usb_unlink_td
; 3. Get number of bytes that remain to be transferred. ; 3. Get number of bytes that remain to be transferred.
; If CurBufPtr is zero, everything was transferred. ; If CurBufPtr is zero, everything was transferred.
xor edx, edx xor edx, edx
@ -1429,30 +1470,19 @@ proc ohci_process_finalized_td
neg edx neg edx
; 4. Check for error. If so, go to 7. ; 4. Check for error. If so, go to 7.
push ebx push ebx
mov eax, [ebx+ohci_gtd.Flags-sizeof.ohci_gtd] mov ecx, [ebx+ohci_gtd.Flags-sizeof.ohci_gtd]
shr eax, 28 shr ecx, 28
jnz .error jnz .error
.notify: .notify:
; 5. Successful completion. ; 5. Successful completion.
; 5a. Check whether this descriptor has an associated callback. invoke usbhc_api.usb_process_gtd
mov ecx, [ebx+usb_gtd.Callback]
test ecx, ecx
jz .ok_nocallback
; 5b. If so, call the callback.
stdcall_verify ecx, [ebx+usb_gtd.Pipe], eax, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
jmp .next_td
.ok_nocallback:
; 5c. Otherwise, add length of the current descriptor to the next descriptor.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
.next_td: .next_td:
; 6. Free the current descriptor and advance to the next item. ; 6. Free the current descriptor and advance to the next item.
; If the current item is the last in the list, ; If the current item is the last in the list,
; set DoneListEndPtr to pointer to DoneList. ; set DoneListEndPtr to pointer to DoneList.
cmp ebx, [esp] cmp ebx, [esp]
jz @f jz @f
stdcall usb1_free_general_td, ebx stdcall ohci_free_gtd, ebx
@@: @@:
pop ebx pop ebx
lea eax, [ebx+ohci_gtd.NextTD-sizeof.ohci_gtd] lea eax, [ebx+ohci_gtd.NextTD-sizeof.ohci_gtd]
@ -1477,7 +1507,7 @@ proc ohci_process_finalized_td
; Free the current item, set ebx to the next item, continue to 5a. ; Free the current item, set ebx to the next item, continue to 5a.
test ebx, ebx test ebx, ebx
jz @f jz @f
stdcall usb1_free_general_td, ebx stdcall ohci_free_gtd, ebx
@@: @@:
pop ebx pop ebx
ret ret
@ -1485,7 +1515,7 @@ proc ohci_process_finalized_td
; 7. There was an error while processing this descriptor. ; 7. There was an error while processing this descriptor.
; The hardware has stopped processing the queue. ; The hardware has stopped processing the queue.
; 7a. Save status and length. ; 7a. Save status and length.
push eax push ecx
push edx push edx
; DEBUGF 1,'K : TD failed:\n' ; DEBUGF 1,'K : TD failed:\n'
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-sizeof.ohci_gtd],[ebx-sizeof.ohci_gtd+4],[ebx-sizeof.ohci_gtd+8],[ebx-sizeof.ohci_gtd+12] ; DEBUGF 1,'K : %x %x %x %x\n',[ebx-sizeof.ohci_gtd],[ebx-sizeof.ohci_gtd+4],[ebx-sizeof.ohci_gtd+8],[ebx-sizeof.ohci_gtd+12]
@ -1496,7 +1526,7 @@ proc ohci_process_finalized_td
; for this transfer. ; for this transfer.
; Free and unlink non-final descriptors, except the current one. ; Free and unlink non-final descriptors, except the current one.
; Final descriptor will be freed in step 6. ; Final descriptor will be freed in step 6.
call usb_is_final_packet invoke usbhc_api.usb_is_final_packet
jnc .found_final jnc .found_final
mov ebx, [ebx+usb_gtd.NextVirt] mov ebx, [ebx+usb_gtd.NextVirt]
virtual at esp virtual at esp
@ -1505,11 +1535,11 @@ virtual at esp
.current_item dd ? .current_item dd ?
end virtual end virtual
.look_final: .look_final:
call usb_unlink_td invoke usbhc_api.usb_unlink_td
call usb_is_final_packet invoke usbhc_api.usb_is_final_packet
jnc .found_final jnc .found_final
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
stdcall usb1_free_general_td, ebx stdcall ohci_free_gtd, ebx
pop ebx pop ebx
jmp .look_final jmp .look_final
.found_final: .found_final:
@ -1530,14 +1560,14 @@ end virtual
.advance_queue: .advance_queue:
mov eax, [ebx+usb_gtd.NextVirt] mov eax, [ebx+usb_gtd.NextVirt]
sub eax, sizeof.ohci_gtd sub eax, sizeof.ohci_gtd
call get_phys_addr invoke GetPhysAddr
or eax, edx or eax, edx
mov [ecx+ohci_pipe.HeadP-sizeof.ohci_pipe], eax mov [ecx+ohci_pipe.HeadP-sizeof.ohci_pipe], eax
push ebx push ebx
mov ebx, ecx mov ebx, ecx
call ohci_notify_new_work call ohci_notify_new_work
pop ebx pop ebx
pop edx eax pop edx ecx
jmp .notify jmp .notify
; 7d. Abort the entire transfer. ; 7d. Abort the entire transfer.
; There are two cases: either there is only one transfer stage ; There are two cases: either there is only one transfer stage
@ -1553,10 +1583,10 @@ end virtual
cmp ebx, [.current_item] cmp ebx, [.current_item]
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
jz @f jz @f
stdcall usb1_free_general_td, ebx stdcall ohci_free_gtd, ebx
@@: @@:
pop ebx pop ebx
call usb_unlink_td invoke usbhc_api.usb_unlink_td
.halted: .halted:
; 7e. For bulk/interrupt transfers we have no choice but halt the queue, ; 7e. For bulk/interrupt transfers we have no choice but halt the queue,
; the driver should intercede (through some API which is not written yet). ; the driver should intercede (through some API which is not written yet).
@ -1597,3 +1627,60 @@ proc ohci_unlink_pipe
mov [eax+ohci_pipe.NextED-sizeof.ohci_pipe], edx mov [eax+ohci_pipe.NextED-sizeof.ohci_pipe], edx
ret ret
endp endp
; Allocates one endpoint structure for OHCI.
; Returns pointer to software part (usb_pipe) in eax.
proc ohci_alloc_pipe
push ebx
mov ebx, ohci_ep_mutex
invoke usbhc_api.usb_allocate_common, (sizeof.ohci_pipe + sizeof.usb_pipe + 0Fh) and not 0Fh
test eax, eax
jz @f
add eax, sizeof.ohci_pipe
@@:
pop ebx
ret
endp
; Free one endpoint structure for OHCI.
; Stdcall with one argument, pointer to software part (usb_pipe).
proc ohci_free_pipe
sub dword [esp+4], sizeof.ohci_pipe
jmp [usbhc_api.usb_free_common]
endp
; Allocates one general transfer descriptor structure for OHCI.
; Returns pointer to software part (usb_gtd) in eax.
proc ohci_alloc_gtd
push ebx
mov ebx, ohci_gtd_mutex
invoke usbhc_api.usb_allocate_common, (sizeof.ohci_gtd + sizeof.usb_gtd + 0Fh) and not 0Fh
test eax, eax
jz @f
add eax, sizeof.ohci_gtd
@@:
pop ebx
ret
endp
; Free one general transfer descriptor structure for OHCI.
; Stdcall with one argument, pointer to software part (usb_gtd).
proc ohci_free_gtd
sub dword [esp+4], sizeof.ohci_gtd
jmp [usbhc_api.usb_free_common]
endp
include 'usb1_scheduler.inc'
define_controller_name ohci
section '.data' readable writable
include '../peimport.inc'
include_debug_strings
IncludeIGlobals
IncludeUGlobals
align 4
usbhc_api usbhc_func
ohci_ep_first_page dd ?
ohci_ep_mutex MUTEX
ohci_gtd_first_page dd ?
ohci_gtd_mutex MUTEX

View File

@ -1,7 +1,17 @@
; Code for UHCI controllers. ; Code for UHCI controllers.
; Note: it should be moved to an external driver,
; it was convenient to have this code compiled into the kernel during initial ; Standard driver stuff
; development, but there are no reasons to keep it here. format PE DLL native
entry start
__DEBUG__ equ 1
__DEBUG_LEVEL__ equ 1
section '.reloc' data readable discardable fixups
section '.text' code readable executable
include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../../kernel/trunk/bus/usb/common.inc'
; ============================================================================= ; =============================================================================
; ================================= Constants ================================= ; ================================= Constants =================================
@ -145,6 +155,8 @@ DeferredActions dd ?
LastPollTime dd ? LastPollTime dd ?
; See the comment before UHCI_POLL_INTERVAL. This variable keeps the ; See the comment before UHCI_POLL_INTERVAL. This variable keeps the
; last time, in timer ticks, when the polling was done. ; last time, in timer ticks, when the polling was done.
EhciCompanion dd ?
; Pointer to usb_controller for EHCI companion, if any, or NULL.
ends ends
if uhci_controller.IntEDs mod 16 if uhci_controller.IntEDs mod 16
@ -242,8 +254,10 @@ ends
iglobal iglobal
align 4 align 4
uhci_hardware_func: uhci_hardware_func:
dd USBHC_VERSION
dd 'UHCI' dd 'UHCI'
dd sizeof.uhci_controller dd sizeof.uhci_controller
dd uhci_kickoff_bios
dd uhci_init dd uhci_init
dd uhci_process_deferred dd uhci_process_deferred
dd uhci_set_device_address dd uhci_set_device_address
@ -251,21 +265,50 @@ uhci_hardware_func:
dd uhci_port_disable dd uhci_port_disable
dd uhci_new_port.reset dd uhci_new_port.reset
dd uhci_set_endpoint_packet_size dd uhci_set_endpoint_packet_size
dd usb1_allocate_endpoint dd uhci_alloc_pipe
dd uhci_free_pipe dd uhci_free_pipe
dd uhci_init_pipe dd uhci_init_pipe
dd uhci_unlink_pipe dd uhci_unlink_pipe
dd usb1_allocate_general_td dd uhci_alloc_td
dd uhci_free_td dd uhci_free_td
dd uhci_alloc_transfer dd uhci_alloc_transfer
dd uhci_insert_transfer dd uhci_insert_transfer
dd uhci_new_device dd uhci_new_device
uhci_name db 'UHCI',0
endg endg
; ============================================================================= ; =============================================================================
; =================================== Code ==================================== ; =================================== Code ====================================
; ============================================================================= ; =============================================================================
; Called once when driver is loading and once at shutdown.
; When loading, must initialize itself, register itself in the system
; and return eax = value obtained when registering.
proc start
virtual at esp
dd ? ; return address
.reason dd ? ; DRV_ENTRY or DRV_EXIT
.cmdline dd ? ; normally NULL
end virtual
cmp [.reason], DRV_ENTRY
jnz .nothing
mov ecx, uhci_ep_mutex
and dword [ecx-4], 0
invoke MutexInit
mov ecx, uhci_gtd_mutex
and dword [ecx-4], 0
invoke MutexInit
push esi edi
mov esi, [USBHCFunc]
mov edi, usbhc_api
movi ecx, sizeof.usbhc_func/4
rep movsd
pop edi esi
invoke RegUSBDriver, uhci_name, 0, uhci_hardware_func
.nothing:
ret
endp
; Controller-specific initialization function. ; Controller-specific initialization function.
; Called from usb_init_controller. Initializes the hardware and ; Called from usb_init_controller. Initializes the hardware and
; UHCI-specific parts of software structures. ; UHCI-specific parts of software structures.
@ -299,7 +342,7 @@ if (uhci_controller.IntEDs mod 0x1000) <> 0
.err assertion failed .err assertion failed
end if end if
add eax, uhci_controller.IntEDs add eax, uhci_controller.IntEDs
call get_phys_addr invoke GetPhysAddr
; 2b. Fill first 32 entries. ; 2b. Fill first 32 entries.
inc eax inc eax
inc eax ; set QH bit for uhci_pipe.NextQH inc eax ; set QH bit for uhci_pipe.NextQH
@ -349,35 +392,20 @@ end if
call uhci_init_static_endpoint call uhci_init_static_endpoint
; 4. Get I/O base address and size from PCI bus. ; 4. Get I/O base address and size from PCI bus.
; 4a. Read&save PCI command state. ; 4a. Read&save PCI command state.
mov bh, [.devfn] invoke PciRead16, dword [.bus], dword [.devfn], 4
mov ch, [.bus]
mov cl, 1
mov eax, ecx
mov bl, 4
call pci_read_reg
push eax push eax
; 4b. Disable IO access. ; 4b. Disable IO access.
and al, not 1 and al, not 1
push ecx invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax
xchg eax, ecx
call pci_write_reg
pop ecx
; 4c. Read&save IO base address. ; 4c. Read&save IO base address.
mov eax, ecx invoke PciRead16, dword [.bus], dword [.devfn], 20h
mov bl, 20h
call pci_read_reg
and al, not 3 and al, not 3
xchg eax, edi xchg eax, edi
; now edi = IO base ; now edi = IO base
; 4d. Write 0xffff to IO base address. ; 4d. Write 0xffff to IO base address.
push ecx invoke PciWrite16, dword [.bus], dword [.devfn], 20h, -1
xchg eax, ecx
or ecx, -1
call pci_write_reg
pop ecx
; 4e. Read IO base address. ; 4e. Read IO base address.
mov eax, ecx invoke PciRead16, dword [.bus], dword [.devfn], 20h
call pci_read_reg
and al, not 3 and al, not 3
cwde cwde
not eax not eax
@ -385,18 +413,11 @@ end if
xchg eax, esi xchg eax, esi
; now esi = IO size ; now esi = IO size
; 4f. Restore IO base address. ; 4f. Restore IO base address.
xchg eax, ecx invoke PciWrite16, dword [.bus], dword [.devfn], 20h, edi
mov ecx, edi
push eax
call pci_write_reg
pop eax
; 4g. Restore PCI command state and enable io & bus master access. ; 4g. Restore PCI command state and enable io & bus master access.
pop ecx pop ecx
or ecx, 5 or ecx, 5
mov bl, 4 invoke PciWrite16, dword [.bus], dword [.devfn], 4, ecx
push eax
call pci_write_reg
pop eax
; 5. Reset the controller. ; 5. Reset the controller.
; 5e. Host reset. ; 5e. Host reset.
mov edx, edi mov edx, edi
@ -407,7 +428,7 @@ end if
@@: @@:
push esi push esi
movi esi, 1 movi esi, 1
call delay_ms invoke Sleep
pop esi pop esi
in ax, dx in ax, dx
test al, 2 test al, 2
@ -421,7 +442,7 @@ if 0
; wait 10 ms ; wait 10 ms
push esi push esi
movi esi, 10 movi esi, 10
call delay_ms invoke Sleep
pop esi pop esi
; clear reset signal ; clear reset signal
xor eax, eax xor eax, eax
@ -458,56 +479,58 @@ end if
pop [esi+usb_controller.NumPorts] pop [esi+usb_controller.NumPorts]
DEBUGF 1,'K : UHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] DEBUGF 1,'K : UHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts]
mov [esi+uhci_controller.IOBase-sizeof.uhci_controller], edi mov [esi+uhci_controller.IOBase-sizeof.uhci_controller], edi
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax
; 8. Hook interrupt. ; 8. Find the EHCI companion.
mov ah, [.bus] ; If there is one, check whether all ports are covered by that companion.
mov al, 0 ; Note: this assumes that EHCI is initialized before USB1 companions.
mov bh, [.devfn] mov ebx, dword [.devfn]
mov bl, 3Ch invoke usbhc_api.usb_find_ehci_companion
call pci_read_reg mov [esi+uhci_controller.EhciCompanion-sizeof.uhci_controller], eax
; 9. Hook interrupt.
invoke PciRead8, dword [.bus], dword [.devfn], 3Ch
; al = IRQ ; al = IRQ
; DEBUGF 1,'K : UHCI %x: io=%x, irq=%x\n',esi,edi,al ; DEBUGF 1,'K : UHCI %x: io=%x, irq=%x\n',esi,edi,al
movzx eax, al movzx eax, al
stdcall attach_int_handler, eax, uhci_irq, esi invoke AttachIntHandler, eax, uhci_irq, esi
; 9. Setup controller registers. ; 10. Setup controller registers.
xor eax, eax xor eax, eax
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
; 9a. UhciStatusReg := 3Fh: clear all status bits ; 10a. UhciStatusReg := 3Fh: clear all status bits
; (for this register 1 clears the corresponding bit, 0 does not change it). ; (for this register 1 clears the corresponding bit, 0 does not change it).
inc edx inc edx
inc edx ; UhciStatusReg == 2 inc edx ; UhciStatusReg == 2
mov al, 3Fh mov al, 3Fh
out dx, ax out dx, ax
; 9b. UhciInterruptReg := 0Dh. ; 10b. UhciInterruptReg := 0Dh.
inc edx inc edx
inc edx ; UhciInterruptReg == 4 inc edx ; UhciInterruptReg == 4
mov al, 0Dh mov al, 0Dh
out dx, ax out dx, ax
; 9c. UhciFrameNumberReg := 0. ; 10c. UhciFrameNumberReg := 0.
inc edx inc edx
inc edx ; UhciFrameNumberReg == 6 inc edx ; UhciFrameNumberReg == 6
mov al, 0 mov al, 0
out dx, ax out dx, ax
; 9d. UhciBaseAddressReg := physical address of uhci_controller. ; 10d. UhciBaseAddressReg := physical address of uhci_controller.
inc edx inc edx
inc edx ; UhciBaseAddressReg == 8 inc edx ; UhciBaseAddressReg == 8
lea eax, [esi-sizeof.uhci_controller] lea eax, [esi-sizeof.uhci_controller]
call get_phys_addr invoke GetPhysAddr
out dx, eax out dx, eax
; 9e. UhciCommandReg := Run + Configured + (MaxPacket is 64 bytes) ; 10e. UhciCommandReg := Run + Configured + (MaxPacket is 64 bytes)
sub edx, UhciBaseAddressReg ; UhciCommandReg == 0 sub edx, UhciBaseAddressReg ; UhciCommandReg == 0
mov ax, 0C1h ; Run, Configured, MaxPacket = 64b mov ax, 0C1h ; Run, Configured, MaxPacket = 64b
out dx, ax out dx, ax
; 10. Do initial scan of existing devices. ; 11. Do initial scan of existing devices.
call uhci_poll_roothub call uhci_poll_roothub
; 11. Return pointer to usb_controller. ; 12. Return pointer to usb_controller.
xchg eax, esi xchg eax, esi
ret ret
.fail: .fail:
; On error, pop the pointer saved at step 1 and return zero. ; On error, pop the pointer saved at step 1 and return zero.
; Note that the main code branch restores the stack at step 7 and never fails ; Note that the main code branch restores the stack at step 8 and never fails
; after step 7. ; after step 8.
pop ecx pop ecx
xor eax, eax xor eax, eax
ret ret
@ -518,11 +541,7 @@ endp
; so do it in inpolite way, preventing controller from any SMI activity. ; so do it in inpolite way, preventing controller from any SMI activity.
proc uhci_kickoff_bios proc uhci_kickoff_bios
; 1. Get the I/O address. ; 1. Get the I/O address.
mov ah, [esi+PCIDEV.bus] invoke PciRead16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 20h
mov al, 1
mov bh, [esi+PCIDEV.devfn]
mov bl, 20h
call pci_read_reg
and eax, 0xFFFC and eax, 0xFFFC
xchg eax, edx xchg eax, edx
; 2. Stop the controller and disable all interrupts. ; 2. Stop the controller and disable all interrupts.
@ -534,11 +553,7 @@ proc uhci_kickoff_bios
out dx, ax out dx, ax
; 3. Disable all bits for SMI routing, clear SMI routing status, ; 3. Disable all bits for SMI routing, clear SMI routing status,
; enable master interrupt bit. ; enable master interrupt bit.
mov ah, [esi+PCIDEV.bus] invoke PciWrite16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 0xC0, 0AF00h
mov al, 1
mov bl, 0xC0
mov ecx, 0AF00h
call pci_write_reg
ret ret
endp endp
@ -552,7 +567,7 @@ proc uhci_init_static_endpoint
mov byte [edi+uhci_static_ep.HeadTD], 1 mov byte [edi+uhci_static_ep.HeadTD], 1
mov [edi+uhci_static_ep.NextList], esi mov [edi+uhci_static_ep.NextList], esi
add edi, uhci_static_ep.SoftwarePart add edi, uhci_static_ep.SoftwarePart
call usb_init_static_endpoint invoke usbhc_api.usb_init_static_endpoint
add edi, sizeof.uhci_static_ep - uhci_static_ep.SoftwarePart add edi, sizeof.uhci_static_ep - uhci_static_ep.SoftwarePart
ret ret
endp endp
@ -612,7 +627,7 @@ end virtual
push ebx push ebx
xor ebx, ebx xor ebx, ebx
inc ebx inc ebx
call usb_wakeup_if_needed invoke usbhc_api.usb_wakeup_if_needed
pop ebx pop ebx
; 6. This is our interrupt; return 1. ; 6. This is our interrupt; return 1.
mov al, 1 mov al, 1
@ -633,12 +648,12 @@ proc uhci_process_deferred
; the error can be caused by disconnect, try to detect it. ; the error can be caused by disconnect, try to detect it.
test byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], 2 test byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], 2
jnz .force_poll jnz .force_poll
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+uhci_controller.LastPollTime-sizeof.uhci_controller] sub eax, [esi+uhci_controller.LastPollTime-sizeof.uhci_controller]
sub eax, UHCI_POLL_INTERVAL sub eax, UHCI_POLL_INTERVAL
jl .nopoll jl .nopoll
.force_poll: .force_poll:
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax
call uhci_poll_roothub call uhci_poll_roothub
mov eax, -UHCI_POLL_INTERVAL mov eax, -UHCI_POLL_INTERVAL
@ -675,7 +690,7 @@ proc uhci_process_deferred
@@: @@:
; 4. Process disconnect events. This should be done after step 2 ; 4. Process disconnect events. This should be done after step 2
; (which includes the first stage of disconnect processing). ; (which includes the first stage of disconnect processing).
call usb_disconnect_stage2 invoke usbhc_api.usb_disconnect_stage2
; 5. Test whether USB_CONNECT_DELAY for a connected device is over. ; 5. Test whether USB_CONNECT_DELAY for a connected device is over.
; Call uhci_new_port for all such devices. ; Call uhci_new_port for all such devices.
xor ecx, ecx xor ecx, ecx
@ -684,7 +699,15 @@ proc uhci_process_deferred
.portloop: .portloop:
bt [esi+usb_controller.NewConnected], ecx bt [esi+usb_controller.NewConnected], ecx
jnc .noconnect jnc .noconnect
mov eax, [timer_ticks] ; If this port is shared with the EHCI companion and we see the connect event,
; then the device is USB1 dropped by EHCI,
; so EHCI has already waited for debounce delay, we can proceed immediately.
cmp [esi+uhci_controller.EhciCompanion-sizeof.uhci_controller], 0
jz .portloop.test_time
dbgstr 'port is shared with EHCI, skipping initial debounce'
jmp .connected
.portloop.test_time:
invoke GetTimerTicks
sub eax, [esi+usb_controller.ConnectedTime+ecx*4] sub eax, [esi+usb_controller.ConnectedTime+ecx*4]
sub eax, USB_CONNECT_DELAY sub eax, USB_CONNECT_DELAY
jge .connected jge .connected
@ -721,7 +744,7 @@ proc uhci_process_deferred
cmp [esi+usb_controller.ResettingStatus], 1 cmp [esi+usb_controller.ResettingStatus], 1
jnz .no_reset_in_progress jnz .no_reset_in_progress
; 7b. Yep. Test whether it should be stopped. ; 7b. Yep. Test whether it should be stopped.
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+usb_controller.ResetTime] sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_TIME sub eax, USB_RESET_TIME
jge .reset_done jge .reset_done
@ -739,7 +762,7 @@ proc uhci_process_deferred
cmp [esi+usb_controller.ResettingStatus], 0 cmp [esi+usb_controller.ResettingStatus], 0
jz .skip_reset jz .skip_reset
; 7f. Yep. Test whether it should be stopped. ; 7f. Yep. Test whether it should be stopped.
mov eax, [timer_ticks] invoke GetTimerTicks
sub eax, [esi+usb_controller.ResetTime] sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME sub eax, USB_RESET_RECOVERY_TIME
jge .reset_recovery_done jge .reset_recovery_done
@ -758,7 +781,7 @@ proc uhci_process_deferred
; 8. Process wait-done notifications, test for new wait requests. ; 8. Process wait-done notifications, test for new wait requests.
; Note: that must be done after steps 4 and 6 which could create new requests. ; Note: that must be done after steps 4 and 6 which could create new requests.
; 8a. Call the worker function. ; 8a. Call the worker function.
call usb_process_wait_lists invoke usbhc_api.usb_process_wait_lists
; 8b. If no new requests, skip the rest of this step. ; 8b. If no new requests, skip the rest of this step.
test eax, eax test eax, eax
jz @f jz @f
@ -832,7 +855,7 @@ proc uhci_process_updated_list
; of the queue until a) the last descriptor (not the part of the queue itself) ; of the queue until a) the last descriptor (not the part of the queue itself)
; or b) an active (not yet processed by the hardware) descriptor is reached. ; or b) an active (not yet processed by the hardware) descriptor is reached.
lea ecx, [ebx+usb_pipe.Lock] lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock invoke MutexLock
mov ebx, [ebx+usb_pipe.LastTD] mov ebx, [ebx+usb_pipe.LastTD]
push ebx push ebx
mov ebx, [ebx+usb_gtd.NextVirt] mov ebx, [ebx+usb_gtd.NextVirt]
@ -847,13 +870,13 @@ proc uhci_process_updated_list
; Release the queue lock while processing one descriptor: ; Release the queue lock while processing one descriptor:
; callback function could (and often would) schedule another transfer. ; callback function could (and often would) schedule another transfer.
push ecx push ecx
call mutex_unlock invoke MutexUnlock
call uhci_process_finalized_td call uhci_process_finalized_td
pop ecx pop ecx
call mutex_lock invoke MutexLock
jmp .tdloop jmp .tdloop
.tddone: .tddone:
call mutex_unlock invoke MutexUnlock
pop ebx pop ebx
; End of internal loop, restore pointer to the next pipe ; End of internal loop, restore pointer to the next pipe
; and continue the external loop. ; and continue the external loop.
@ -871,7 +894,7 @@ endp
; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd. ; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd.
proc uhci_process_finalized_td proc uhci_process_finalized_td
; 1. Remove this descriptor from the list of descriptors for this pipe. ; 1. Remove this descriptor from the list of descriptors for this pipe.
call usb_unlink_td invoke usbhc_api.usb_unlink_td
; DEBUGF 1,'K : finalized TD:\n' ; DEBUGF 1,'K : finalized TD:\n'
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-20],[ebx-16],[ebx-12],[ebx-8] ; DEBUGF 1,'K : %x %x %x %x\n',[ebx-20],[ebx-16],[ebx-12],[ebx-8]
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-4],[ebx],[ebx+4],[ebx+8] ; DEBUGF 1,'K : %x %x %x %x\n',[ebx-4],[ebx],[ebx+4],[ebx+8]
@ -958,12 +981,12 @@ end if
; for this transfer. ; for this transfer.
; Free and unlink non-final descriptors, except the current one. ; Free and unlink non-final descriptors, except the current one.
; Final descriptor will be freed in step 7. ; Final descriptor will be freed in step 7.
call usb_is_final_packet invoke usbhc_api.usb_is_final_packet
jnc .found_final jnc .found_final
mov ebx, [ebx+usb_gtd.NextVirt] mov ebx, [ebx+usb_gtd.NextVirt]
.look_final: .look_final:
call usb_unlink_td invoke usbhc_api.usb_unlink_td
call usb_is_final_packet invoke usbhc_api.usb_is_final_packet
jnc .found_final jnc .found_final
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
stdcall uhci_free_td, ebx stdcall uhci_free_td, ebx
@ -1040,7 +1063,7 @@ end if
stdcall uhci_free_td, ebx stdcall uhci_free_td, ebx
@@: @@:
pop ebx pop ebx
call usb_unlink_td invoke usbhc_api.usb_unlink_td
pop ecx pop ecx
.normal: .normal:
; 5g. For bulk/interrupt transfers we have no choice but halt the queue, ; 5g. For bulk/interrupt transfers we have no choice but halt the queue,
@ -1062,21 +1085,7 @@ end if
; 6. Either the descriptor in ebx was processed without errors, ; 6. Either the descriptor in ebx was processed without errors,
; or all necessary error actions were taken and ebx points to the last ; or all necessary error actions were taken and ebx points to the last
; related descriptor. ; related descriptor.
; 6a. Test whether it is the last packet in the transfer invoke usbhc_api.usb_process_gtd
; <=> it has an associated callback.
mov eax, [ebx+usb_gtd.Callback]
test eax, eax
jz .nocallback
; 6b. It has an associated callback; call it with corresponding parameters.
stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
jmp .callback
.nocallback:
; 6c. It is an intermediate packet. Add its length to the length
; in the following packet.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
.callback:
; 7. Free the current descriptor (if allowed) and return the next one. ; 7. Free the current descriptor (if allowed) and return the next one.
; 7a. Save pointer to the next descriptor. ; 7a. Save pointer to the next descriptor.
push [ebx+usb_gtd.NextVirt] push [ebx+usb_gtd.NextVirt]
@ -1113,7 +1122,7 @@ proc uhci_fix_toggle
jz .nothing jz .nothing
; 3. Lock the transfer queue. ; 3. Lock the transfer queue.
add ecx, usb_pipe.Lock add ecx, usb_pipe.Lock
call mutex_lock invoke MutexLock
; 4. Flip the toggle bit in all packets from ebx.NextVirt to ecx.LastTD ; 4. Flip the toggle bit in all packets from ebx.NextVirt to ecx.LastTD
; (inclusive). ; (inclusive).
mov eax, [ebx+usb_gtd.NextVirt] mov eax, [ebx+usb_gtd.NextVirt]
@ -1126,7 +1135,7 @@ proc uhci_fix_toggle
xor byte [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock+2], 1 shl (19-16) xor byte [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock+2], 1 shl (19-16)
or dword [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock], eax or dword [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock], eax
; 6. Unlock the transfer queue. ; 6. Unlock the transfer queue.
call mutex_unlock invoke MutexUnlock
.nothing: .nothing:
ret ret
endp endp
@ -1164,14 +1173,14 @@ proc uhci_poll_roothub
; Some controllers set enable status change bit, some don't. ; Some controllers set enable status change bit, some don't.
test bl, 2 test bl, 2
jz .noconnectchange jz .noconnectchange
DEBUGF 1,'K : [%d] UHCI %x connect status changed, %x/%x\n',[timer_ticks],esi,bx,ax DEBUGF 1,'K : UHCI %x connect status changed, %x/%x\n',esi,bx,ax
; yep. Regardless of the current status, note disconnect event; ; yep. Regardless of the current status, note disconnect event;
; if there is something connected, store the connect time and note connect event. ; if there is something connected, store the connect time and note connect event.
; In any way, do not process ; In any way, do not process
bts [esi+usb_controller.NewDisconnected], ecx bts [esi+usb_controller.NewDisconnected], ecx
test al, 1 test al, 1
jz .disconnect jz .disconnect
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ConnectedTime+ecx*4], eax mov [esi+usb_controller.ConnectedTime+ecx*4], eax
bts [esi+usb_controller.NewConnected], ecx bts [esi+usb_controller.NewConnected], ecx
jmp .nextport jmp .nextport
@ -1222,7 +1231,7 @@ proc uhci_new_port
or ah, 2 or ah, 2
out dx, ax out dx, ax
; 3. Store the current time and set status to 1 = reset signalling active. ; 3. Store the current time and set status to 1 = reset signalling active.
mov eax, [timer_ticks] invoke GetTimerTicks
mov [esi+usb_controller.ResetTime], eax mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 1 mov [esi+usb_controller.ResettingStatus], 1
.nothing: .nothing:
@ -1237,14 +1246,14 @@ proc uhci_port_reset_done
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+ecx*2+UhciPort1StatusReg] lea edx, [edx+ecx*2+UhciPort1StatusReg]
in ax, dx in ax, dx
DEBUGF 1,'K : [%d] UHCI %x status %x/',[timer_ticks],esi,ax DEBUGF 1,'K : UHCI %x status %x/',esi,ax
and ah, not 2 and ah, not 2
out dx, ax out dx, ax
; 2. Status bits in UHCI are invalid during reset signalling. ; 2. Status bits in UHCI are invalid during reset signalling.
; Wait a millisecond while status bits become valid again. ; Wait a millisecond while status bits become valid again.
push esi push esi
movi esi, 1 movi esi, 1
call delay_ms invoke Sleep
pop esi pop esi
; 3. ConnectStatus bit is zero during reset and becomes 1 during step 2; ; 3. ConnectStatus bit is zero during reset and becomes 1 during step 2;
; some controllers interpret this as a (fake) connect event. ; some controllers interpret this as a (fake) connect event.
@ -1254,8 +1263,8 @@ proc uhci_port_reset_done
or al, 6 ; enable port, clear status change or al, 6 ; enable port, clear status change
out dx, ax out dx, ax
; 4. Store the current time and set status to 2 = reset recovery active. ; 4. Store the current time and set status to 2 = reset recovery active.
mov eax, [timer_ticks] invoke GetTimerTicks
DEBUGF 1,'K : reset done at %d\n',[timer_ticks] DEBUGF 1,'K : reset done\n'
mov [esi+usb_controller.ResetTime], eax mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 2 mov [esi+usb_controller.ResettingStatus], 2
ret ret
@ -1272,12 +1281,12 @@ proc uhci_port_init
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+ecx*2+UhciPort1StatusReg] lea edx, [edx+ecx*2+UhciPort1StatusReg]
in ax, dx in ax, dx
DEBUGF 1,'K : [%d] UHCI %x status %x\n',[timer_ticks],esi,ax DEBUGF 1,'K : UHCI %x status %x\n',esi,ax
; 2. If the device has been disconnected, stop the initialization. ; 2. If the device has been disconnected, stop the initialization.
test al, 1 test al, 1
jnz @f jnz @f
dbgstr 'USB port disabled after reset' dbgstr 'USB port disabled after reset'
jmp usb_test_pending_port jmp [usbhc_api.usb_test_pending_port]
@@: @@:
; 3. Copy LowSpeed bit to bit 0 of eax and call the worker procedure ; 3. Copy LowSpeed bit to bit 0 of eax and call the worker procedure
; to notify the protocol layer about new UHCI device. ; to notify the protocol layer about new UHCI device.
@ -1293,7 +1302,7 @@ proc uhci_port_init
in ax, dx in ax, dx
and al, not 4 and al, not 4
out dx, ax ; disable the port out dx, ax ; disable the port
jmp usb_test_pending_port jmp [usbhc_api.usb_test_pending_port]
.nothing: .nothing:
ret ret
endp endp
@ -1316,7 +1325,7 @@ proc uhci_new_device
push eax ; ignored (ErrorTD) push eax ; ignored (ErrorTD)
push eax ; .Token field: DeviceAddress is zero, bit 20 = LowSpeedDevice push eax ; .Token field: DeviceAddress is zero, bit 20 = LowSpeedDevice
; 4. Notify the protocol layer. ; 4. Notify the protocol layer.
call usb_new_device invoke usbhc_api.usb_new_device
; 5. Cleanup the stack after step 3 and return. ; 5. Cleanup the stack after step 3 and return.
add esp, 12 add esp, 12
ret ret
@ -1327,8 +1336,7 @@ endp
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
proc uhci_set_device_address proc uhci_set_device_address
mov byte [ebx+uhci_pipe.Token+1-sizeof.uhci_pipe], cl mov byte [ebx+uhci_pipe.Token+1-sizeof.uhci_pipe], cl
call usb_subscription_done jmp [usbhc_api.usb_subscription_done]
ret
endp endp
; This procedure returns USB device address from the uhci_pipe structure. ; This procedure returns USB device address from the uhci_pipe structure.
@ -1364,8 +1372,7 @@ proc uhci_set_endpoint_packet_size
or [ebx+uhci_pipe.Token-sizeof.uhci_pipe], ecx or [ebx+uhci_pipe.Token-sizeof.uhci_pipe], ecx
; uhci_pipe.Token field is purely for software bookkeeping and does not affect ; uhci_pipe.Token field is purely for software bookkeeping and does not affect
; the hardware; thus, we can continue initialization immediately. ; the hardware; thus, we can continue initialization immediately.
call usb_subscription_done jmp [usbhc_api.usb_subscription_done]
ret
endp endp
; This procedure is called from API usb_open_pipe and processes ; This procedure is called from API usb_open_pipe and processes
@ -1392,7 +1399,7 @@ end virtual
; 2. Initialize HeadTD to the physical address of the first TD. ; 2. Initialize HeadTD to the physical address of the first TD.
push eax ; store pointer to the first TD for step 4 push eax ; store pointer to the first TD for step 4
sub eax, sizeof.uhci_gtd sub eax, sizeof.uhci_gtd
call get_phys_addr invoke GetPhysAddr
mov [edi+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax mov [edi+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax
; 3. Initialize Token field: ; 3. Initialize Token field:
; take DeviceAddress and LowSpeedDevice from the parent pipe, ; take DeviceAddress and LowSpeedDevice from the parent pipe,
@ -1470,7 +1477,7 @@ end virtual
mov ecx, [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart] mov ecx, [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart]
mov [edi+uhci_pipe.NextQH-sizeof.uhci_pipe], ecx mov [edi+uhci_pipe.NextQH-sizeof.uhci_pipe], ecx
lea eax, [edi-sizeof.uhci_pipe] lea eax, [edi-sizeof.uhci_pipe]
call get_phys_addr invoke GetPhysAddr
inc eax inc eax
inc eax inc eax
mov [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart], eax mov [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart], eax
@ -1512,18 +1519,6 @@ proc uhci_unlink_pipe
ret ret
endp endp
; Free memory associated with pipe.
; For UHCI, this includes usb_pipe structure and ErrorTD, if present.
proc uhci_free_pipe
mov eax, [esp+4]
mov eax, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe]
test eax, eax
jz @f
stdcall uhci_free_td, eax
@@:
jmp usb1_free_endpoint
endp
; This procedure is called from the several places in main USB code ; This procedure is called from the several places in main USB code
; and allocates required packets for the given transfer stage. ; and allocates required packets for the given transfer stage.
; ebx = pipe, other parameters are passed through the stack ; ebx = pipe, other parameters are passed through the stack
@ -1612,7 +1607,7 @@ endl
.fail: .fail:
mov edi, uhci_hardware_func mov edi, uhci_hardware_func
mov eax, [td] mov eax, [td]
stdcall usb_undo_tds, [origTD] invoke usbhc_api.usb_undo_tds, [origTD]
xor eax, eax xor eax, eax
jmp .nothing jmp .nothing
endp endp
@ -1652,7 +1647,7 @@ end virtual
mov eax, [.packetSize] mov eax, [.packetSize]
add eax, eax add eax, eax
add eax, sizeof.uhci_original_buffer add eax, sizeof.uhci_original_buffer
call malloc invoke Kmalloc
; 1d. If failed, return zero. ; 1d. If failed, return zero.
test eax, eax test eax, eax
jz .nothing jz .nothing
@ -1695,14 +1690,14 @@ end virtual
.notempbuf: .notempbuf:
; 2. Allocate the next TD. ; 2. Allocate the next TD.
push eax push eax
call usb1_allocate_general_td call uhci_alloc_td
pop edx pop edx
; If failed, free the temporary buffer (if it was allocated) and return zero. ; If failed, free the temporary buffer (if it was allocated) and return zero.
test eax, eax test eax, eax
jz .fail jz .fail
; 3. Initialize controller-independent parts of both TDs. ; 3. Initialize controller-independent parts of both TDs.
push edx push edx
call usb_init_transfer invoke usbhc_api.usb_init_transfer
; 4. Initialize the next TD: ; 4. Initialize the next TD:
; mark it as last one (this will be changed when further packets will be ; mark it as last one (this will be changed when further packets will be
; allocated), copy Token field from uhci_pipe.Token zeroing bit 20, ; allocated), copy Token field from uhci_pipe.Token zeroing bit 20,
@ -1725,7 +1720,7 @@ end virtual
; 5b. Store physical address of the next TD. ; 5b. Store physical address of the next TD.
push eax push eax
sub eax, sizeof.uhci_gtd sub eax, sizeof.uhci_gtd
call get_phys_addr invoke GetPhysAddr
; for Control/Bulk pipes, use Depth traversal unless this is the first TD ; for Control/Bulk pipes, use Depth traversal unless this is the first TD
; in the transfer stage; ; in the transfer stage;
; uhci_insert_transfer will set Depth traversal for the first TD and clear ; uhci_insert_transfer will set Depth traversal for the first TD and clear
@ -1748,7 +1743,7 @@ end virtual
jz @f jz @f
mov eax, [edx+uhci_original_buffer.UsedBuffer] mov eax, [edx+uhci_original_buffer.UsedBuffer]
@@: @@:
call get_phys_addr invoke GetPhysAddr
.hasphysbuf: .hasphysbuf:
mov [ecx+uhci_gtd.Buffer-sizeof.uhci_gtd], eax mov [ecx+uhci_gtd.Buffer-sizeof.uhci_gtd], eax
; 5d. For IN transfers, disallow short packets. ; 5d. For IN transfers, disallow short packets.
@ -1774,7 +1769,7 @@ end virtual
ret ret
.fail: .fail:
xchg eax, edx xchg eax, edx
call free invoke Kfree
xor eax, eax xor eax, eax
ret ret
endp endp
@ -1796,6 +1791,47 @@ proc uhci_insert_transfer
ret ret
endp endp
; Allocates one endpoint structure for OHCI.
; Returns pointer to software part (usb_pipe) in eax.
proc uhci_alloc_pipe
push ebx
mov ebx, uhci_ep_mutex
invoke usbhc_api.usb_allocate_common, (sizeof.uhci_pipe + sizeof.usb_pipe + 0Fh) and not 0Fh
test eax, eax
jz @f
add eax, sizeof.uhci_pipe
@@:
pop ebx
ret
endp
; Free memory associated with pipe.
; For UHCI, this includes usb_pipe structure and ErrorTD, if present.
proc uhci_free_pipe
mov eax, [esp+4]
mov eax, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe]
test eax, eax
jz @f
stdcall uhci_free_td, eax
@@:
sub dword [esp+4], sizeof.uhci_pipe
jmp [usbhc_api.usb_free_common]
endp
; Allocates one general transfer descriptor structure for UHCI.
; Returns pointer to software part (usb_gtd) in eax.
proc uhci_alloc_td
push ebx
mov ebx, uhci_gtd_mutex
invoke usbhc_api.usb_allocate_common, (sizeof.uhci_gtd + sizeof.usb_gtd + 0Fh) and not 0Fh
test eax, eax
jz @f
add eax, sizeof.uhci_gtd
@@:
pop ebx
ret
endp
; Free all memory associated with one TD. ; Free all memory associated with one TD.
; For UHCI, this includes memory for uhci_gtd itself ; For UHCI, this includes memory for uhci_gtd itself
; and the temporary buffer, if present. ; and the temporary buffer, if present.
@ -1804,10 +1840,23 @@ proc uhci_free_td
mov eax, [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] mov eax, [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd]
and eax, not 1 and eax, not 1
jz .nobuf jz .nobuf
push ebx invoke Kfree
call free
pop ebx
.nobuf: .nobuf:
sub dword [esp+4], sizeof.uhci_gtd sub dword [esp+4], sizeof.uhci_gtd
jmp usb_free_common jmp [usbhc_api.usb_free_common]
endp endp
include 'usb1_scheduler.inc'
define_controller_name uhci
section '.data' readable writable
include '../peimport.inc'
include_debug_strings
IncludeIGlobals
IncludeUGlobals
align 4
usbhc_api usbhc_func
uhci_ep_first_page dd ?
uhci_ep_mutex MUTEX
uhci_gtd_first_page dd ?
uhci_gtd_mutex MUTEX

View File

@ -0,0 +1,232 @@
; Implementation of periodic transaction scheduler for USB.
; Bandwidth dedicated to periodic transactions is limited, so
; different pipes should be scheduled as uniformly as possible.
; USB1 scheduler.
; Algorithm is simple:
; when adding a pipe, optimize the following quantity:
; * for every millisecond, take all bandwidth scheduled to periodic transfers,
; * calculate maximum over all milliseconds,
; * select a variant which minimizes that maximum;
; when removing a pipe, do nothing (except for bookkeeping).
; The caller must provide CONTROLLER_NAME define.
macro define_controller_name name
{
_hci_static_ep.SoftwarePart = name # _static_ep.SoftwarePart
_hci_static_ep.NextList = name # _static_ep.NextList
sizeof._hci_static_ep = sizeof. # name # _static_ep
}
; Select a list for a new pipe.
; in: esi -> usb_controller, maxpacket, type, interval can be found in the stack
; in: ecx = 2 * maximal interval = total number of periodic lists + 1
; in: edx -> {u|o}hci_static_ep for the first list
; in: eax -> byte past {u|o}hci_static_ep for the last list in the first group
; out: edx -> usb_static_ep for the selected list or zero if failed
proc usb1_select_interrupt_list
; inherit some variables from usb_open_pipe
virtual at ebp-12
.speed db ?
rb 3
.bandwidth dd ?
.target dd ?
dd ?
dd ?
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
push ebx edi ; save used registers to be stdcall
push eax ; save eax for checks in step 3
; 1. Only intervals 2^k ms can be supported.
; The core specification says that the real interval should not be greater
; than the interval given by the endpoint descriptor, but can be less.
; Determine the actual interval as 2^k ms.
mov eax, ecx
; 1a. Set [.interval] to 1 if it was zero; leave it as is otherwise
cmp [.interval], 1
adc [.interval], 0
; 1b. Divide ecx by two while it is strictly greater than [.interval].
@@:
shr ecx, 1
cmp [.interval], ecx
jb @b
; ecx = the actual interval
;
; For example, let ecx = 8, eax = 64.
; The scheduler space is 32 milliseconds,
; we need to schedule something every 8 ms;
; there are 8 variants: schedule at times 0,8,16,24,
; schedule at times 1,9,17,25,..., schedule at times 7,15,23,31.
; Now concentrate: there are three nested loops,
; * the innermost loop calculates the total periodic bandwidth scheduled
; in the given millisecond,
; * the intermediate loop calculates the maximum over all milliseconds
; in the given variant, that is the quantity we're trying to minimize,
; * the outermost loop checks all variants.
; 2. Calculate offset between the first list and the first list for the
; selected interval, in bytes; save in the stack for step 4.
sub eax, ecx
sub eax, ecx
imul eax, sizeof._hci_static_ep
push eax
imul ebx, ecx, sizeof._hci_static_ep
; 3. Select the best variant.
; 3a. The outermost loop.
; Prepare for the loop: set the current optimal bandwidth to maximum
; possible value (so that any variant will pass the first comparison),
; calculate delta for the intermediate loop.
or [.bandwidth], -1
.varloop:
; 3b. The intermediate loop.
; Prepare for the loop: set the maximum to be calculated to zero,
; save counter of the outermost loop.
xor edi, edi
push edx
virtual at esp
.cur_variant dd ? ; step 3b
.result_delta dd ? ; step 2
.group1_limit dd ? ; function prolog
end virtual
.calc_max_bandwidth:
; 3c. The innermost loop. Sum over all lists.
xor eax, eax
push edx
.calc_bandwidth:
add eax, [edx+_hci_static_ep.SoftwarePart+usb_static_ep.Bandwidth]
mov edx, [edx+_hci_static_ep.NextList]
test edx, edx
jnz .calc_bandwidth
pop edx
; 3d. The intermediate loop continued: update maximum.
cmp eax, edi
jb @f
mov edi, eax
@@:
; 3e. The intermediate loop continued: advance counter.
add edx, ebx
cmp edx, [.group1_limit]
jb .calc_max_bandwidth
; 3e. The intermediate loop done: restore counter of the outermost loop.
pop edx
; 3f. The outermost loop continued: if the current variant is
; better (maybe not strictly) then the previous optimum, update
; the optimal bandwidth and resulting list.
cmp edi, [.bandwidth]
ja @f
mov [.bandwidth], edi
mov [.target], edx
@@:
; 3g. The outermost loop continued: advance counter.
add edx, sizeof._hci_static_ep
dec ecx
jnz .varloop
; 4. Calculate bandwidth for the new pipe.
mov eax, [.maxpacket]
mov cl, [.speed]
mov ch, byte [.endpoint]
and ch, 80h
call calc_usb1_bandwidth
; 5. Get the pointer to the best list.
pop edx ; restore value from step 2
pop ecx ; purge stack var from prolog
add edx, [.target]
; 6. Check that bandwidth for the new pipe plus old bandwidth
; still fits to maximum allowed by the core specification, 90% of 12000 bits.
mov ecx, eax
add ecx, [.bandwidth]
cmp ecx, 10800
ja .no_bandwidth
; 7. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return.
add edx, _hci_static_ep.SoftwarePart
add [edx+usb_static_ep.Bandwidth], eax
pop edi ebx ; restore used registers to be stdcall
ret
.no_bandwidth:
dbgstr 'Periodic bandwidth limit reached'
xor edx, edx
pop edi ebx
ret
endp
; Pipe is removing, update the corresponding lists.
; We do not reorder anything, so just update book-keeping variable
; in the list header.
proc usb1_interrupt_list_unlink
virtual at esp
dd ? ; return address
.maxpacket dd ?
.lowspeed db ?
.direction db ?
rb 2
end virtual
; calculate bandwidth on the bus
mov eax, [.maxpacket]
mov ecx, dword [.lowspeed]
call calc_usb1_bandwidth
; find list header
mov edx, ebx
@@:
mov edx, [edx+usb_pipe.NextVirt]
cmp [edx+usb_pipe.Controller], esi
jz @b
; subtract pipe bandwidth
sub [edx+usb_static_ep.Bandwidth], eax
ret 8
endp
; Helper procedure for USB1 scheduler: calculate bandwidth on the bus.
; in: low 11 bits of eax = payload size in bytes
; in: cl = 0 - full-speed, nonzero - high-speed
; in: ch = 0 - OUT, nonzero - IN
; out: eax = maximal bandwidth in FS-bits
proc calc_usb1_bandwidth
and eax, (1 shl 11) - 1 ; get payload for one transaction
add eax, 3 ; add 3 bytes for other fields in data packet, PID+CRC16
test cl, cl
jnz .low_speed
; Multiply by 8 for bytes -> bits, by 7/6 to accomodate bit stuffing
; and by 401/400 for IN transfers to accomodate timers difference
; 9+107/300 for IN transfers, 9+1/3 for OUT transfers
; For 0 <= eax < 09249355h, floor(eax * 107/300) = floor(eax * 5B4E81B5h / 2^32).
; For 0 <= eax < 80000000h, floor(eax / 3) = floor(eax * 55555556h / 2^32).
mov edx, 55555556h
test ch, ch
jz @f
mov edx, 5B4E81B5h
@@:
lea ecx, [eax*9]
mul edx
; Add 93 extra bits: 39 bits for Token packet (8 for SYNC, 24 for token+address,
; 4 extra bits for possible bit stuffing in token+address, 3 for EOP),
; 18 bits for bus turn-around, 11 bits for SYNC+EOP in Data packet plus 1 bit
; for possible timers difference, 2 bits for inter-packet delay, 20 bits for
; Handshake packet, 2 bits for another inter-packet delay.
lea eax, [ecx+edx+93]
ret
.low_speed:
; Multiply by 8 for bytes -> bits, by 7/6 to accomodate bit stuffing,
; by 8 for LS -> FS and by 406/50 for IN transfers to accomodate timers difference.
; 75+59/75 for IN transfers, 74+2/3 for OUT transfers.
mov edx, 0AAAAAABh
test ch, ch
mov ecx, 74
jz @f
mov edx, 0C962FC97h
inc ecx
@@:
imul ecx, eax
mul edx
; Add 778 extra bits:
; 16 bits for PRE packet, 4 bits for hub delay, 8*39 bits for Token packet
; 8*18 bits for bus turn-around
; (406/50)*11 bits for SYNC+EOP in Data packet,
; 8*2 bits for inter-packet delay,
; 16 bits for PRE packet, 4 bits for hub delay, 8*20 bits for Handshake packet,
; 8*2 bits for another inter-packet delay.
lea eax, [ecx+edx+778]
ret
endp

View File

@ -694,7 +694,8 @@ end virtual
mov [ecx+PCIDEV.fd], edi mov [ecx+PCIDEV.fd], edi
mov [eax+PCIDEV.bk], edi mov [eax+PCIDEV.bk], edi
mov eax, dword [.devfn] mov eax, dword [.devfn]
mov word [edi+PCIDEV.devfn], ax mov dword [edi+PCIDEV.devfn], eax
mov dword [edi+PCIDEV.owner], 0
mov bh, al mov bh, al
mov al, 2 mov al, 2
mov bl, 8 mov bl, 8

View File

@ -1,238 +1,33 @@
; USB Host Controller support code: hardware-independent part, ; USB Host Controller support code: hardware-independent part,
; common for all controller types. ; common for all controller types.
; ============================================================================= iglobal
; ================================= Constants ================================= ; USB HC support: some functions interesting only for *HCI-drivers.
; ============================================================================= align 4
; USB device must have at least 100ms of stable power before initializing can usb_hc_func:
; proceed; one timer tick is 10ms, so enforce delay in 10 ticks dd usb_process_gtd
USB_CONNECT_DELAY = 10 dd usb_init_static_endpoint
; USB requires at least 10 ms for reset signalling. Normally, this is one timer dd usb_wakeup_if_needed
; tick. However, it is possible that we start reset signalling in the end of dd usb_subscribe_control
; interval between timer ticks and then we test time in the start of the next dd usb_subscription_done
; interval; in this case, the delta between [timer_ticks] is 1, but the real dd usb_allocate_common
; time passed is significantly less than 10 ms. To avoid this, we add an extra dd usb_free_common
; tick; this guarantees that at least 10 ms have passed. dd usb_td_to_virt
USB_RESET_TIME = 2 dd usb_init_transfer
; USB requires at least 10 ms of reset recovery, a delay between reset dd usb_undo_tds
; signalling and any commands to device. Add an extra tick for the same reasons dd usb_test_pending_port
; as with the previous constant. dd usb_get_tt
USB_RESET_RECOVERY_TIME = 2 dd usb_get_tt_think_time
dd usb_new_device
; ============================================================================= dd usb_disconnect_stage2
; ================================ Structures ================================= dd usb_process_wait_lists
; ============================================================================= dd usb_unlink_td
; Controller descriptor. dd usb_is_final_packet
; This structure represents the common (controller-independent) part dd usb_find_ehci_companion
; of a controller for the USB code. The corresponding controller-dependent endg
; part *hci_controller is located immediately before usb_controller.
struct usb_controller
; Two following fields organize all controllers in the global linked list.
Next dd ?
Prev dd ?
HardwareFunc dd ?
; Pointer to usb_hardware_func structure with controller-specific functions.
NumPorts dd ?
; Number of ports in the root hub.
SetAddressBuffer rb 8
; Buffer for USB control command SET_ADDRESS.
ExistingAddresses rd 128/32
; Bitmask for 128 bits; bit i is cleared <=> address i is free for allocating
; for new devices. Bit 0 is always set.
;
; The hardware is allowed to cache some data from hardware structures.
; Regular operations are designed considering this,
; but sometimes it is required to wait for synchronization of hardware cache
; with modified structures in memory.
; The code keeps two queues of pipes waiting for synchronization,
; one for asynchronous (bulk/control) pipes, one for periodic pipes, hardware
; cache is invalidated under different conditions for those types.
; Both queues are organized in the same way, as single-linked lists.
; There are three special positions: the head of list (new pipes are added
; here), the first pipe to be synchronized at the current iteration,
; the tail of list (all pipes starting from here are synchronized).
WaitPipeListAsync dd ?
WaitPipeListPeriodic dd ?
; List heads.
WaitPipeRequestAsync dd ?
WaitPipeRequestPeriodic dd ?
; Pending request to hardware to refresh cache for items from WaitPipeList*.
; (Pointers to some items in WaitPipeList* or NULLs).
ReadyPipeHeadAsync dd ?
ReadyPipeHeadPeriodic dd ?
; Items of RemovingList* which were released by hardware and are ready
; for further processing.
; (Pointers to some items in WaitPipeList* or NULLs).
NewConnected dd ?
; bit mask of recently connected ports of the root hub,
; bit set = a device was recently connected to the corresponding port;
; after USB_CONNECT_DELAY ticks of stable status these ports are moved to
; PendingPorts
NewDisconnected dd ?
; bit mask of disconnected ports of the root hub,
; bit set = a device in the corresponding port was disconnected,
; disconnect processing is required.
PendingPorts dd ?
; bit mask of ports which are ready to be initialized
ControlLock MUTEX ?
; mutex which guards all operations with control queue
BulkLock MUTEX ?
; mutex which guards all operations with bulk queue
PeriodicLock MUTEX ?
; mutex which guards all operations with periodic queues
WaitSpinlock:
; spinlock guarding WaitPipeRequest/ReadyPipeHead (but not WaitPipeList)
StartWaitFrame dd ?
; USB frame number when WaitPipeRequest* was registered.
ResettingHub dd ?
; Pointer to usb_hub responsible for the currently resetting port, if any.
; NULL for the root hub.
ResettingPort db ?
; Port that is currently resetting, 0-based.
ResettingSpeed db ?
; Speed of currently resetting device.
ResettingStatus db ?
; Status of port reset. 0 = no port is resetting, -1 = reset failed,
; 1 = reset in progress, 2 = reset recovery in progress.
rb 1 ; alignment
ResetTime dd ?
; Time when reset signalling or reset recovery has been started.
ConnectedTime rd 16
; Time, in timer ticks, when the port i has signalled the connect event.
; Valid only if bit i in NewConnected is set.
DevicesByPort rd 16
; Pointer to usb_pipe for zero endpoint (which serves as device handle)
; for each port.
ends
; Interface-specific data. Several interfaces of one device can operate
; independently, each is controlled by some driver and is identified by
; some driver-specific data passed as is to the driver.
struct usb_interface_data
DriverData dd ?
; Passed as is to the driver.
DriverFunc dd ?
; Pointer to USBSRV structure for the driver.
ends
; Device-specific data.
struct usb_device_data
PipeListLock MUTEX
; Lock guarding OpenedPipeList. Must be the first item of the structure,
; the code passes pointer to usb_device_data as is to mutex_lock/unlock.
OpenedPipeList rd 2
; List of all opened pipes for the device.
; Used when the device is disconnected, so all pipes should be closed.
ClosedPipeList rd 2
; List of all closed, but still valid pipes for the device.
; A pipe closed with USBClosePipe is just deallocated,
; but a pipe closed due to disconnect must remain valid until driver-provided
; disconnect handler returns; this list links all such pipes to deallocate them
; after disconnect processing.
NumPipes dd ?
; Number of not-yet-closed pipes.
Hub dd ?
; NULL if connected to the root hub, pointer to usb_hub otherwise.
TTHub dd ?
; Pointer to usb_hub for (the) hub with Transaction Translator for the device,
; NULL if the device operates in the same speed as the controller.
Port db ?
; Port on the hub, zero-based.
TTPort db ?
; Port on the TTHub, zero-based.
DeviceDescrSize db ?
; Size of device descriptor.
Speed db ?
; Device speed, one of USB_SPEED_*.
NumInterfaces dd ?
; Number of interfaces.
ConfigDataSize dd ?
; Total size of data associated with the configuration descriptor
; (including the configuration descriptor itself).
Interfaces dd ?
; Offset from the beginning of this structure to Interfaces field.
; Variable-length fields:
; DeviceDescriptor:
; device descriptor starts here
; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize
; configuration descriptor with all associated data
; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4)
; array of NumInterfaces elements of type usb_interface_data
ends
usb_device_data.DeviceDescriptor = sizeof.usb_device_data
; Description of controller-specific data and functions.
struct usb_hardware_func
ID dd ? ; '*HCI'
DataSize dd ? ; sizeof(*hci_controller)
Init dd ?
; Initialize controller-specific part of controller data.
; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn
; out: eax = 0 <=> failed, otherwise eax -> usb_controller
ProcessDeferred dd ?
; Called regularly from the main loop of USB thread
; (either due to timeout from a previous call, or due to explicit wakeup).
; in: esi -> usb_controller
; out: eax = maximum timeout for next call (-1 = infinity)
SetDeviceAddress dd ?
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
GetDeviceAddress dd ?
; in: esi -> usb_controller, ebx -> usb_pipe
; out: eax = address
PortDisable dd ?
; Disable the given port in the root hub.
; in: esi -> usb_controller, ecx = port (zero-based)
InitiateReset dd ?
; Start reset signalling on the given port.
; in: esi -> usb_controller, ecx = port (zero-based)
SetEndpointPacketSize dd ?
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
AllocPipe dd ?
; out: eax = pointer to allocated usb_pipe
FreePipe dd ?
; void stdcall with one argument = pointer to previously allocated usb_pipe
InitPipe dd ?
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
; esi -> usb_controller, eax -> usb_gtd for the first TD,
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
UnlinkPipe dd ?
; esi -> usb_controller, ebx -> usb_pipe
AllocTD dd ?
; out: eax = pointer to allocated usb_gtd
FreeTD dd ?
; void stdcall with one argument = pointer to previously allocated usb_gtd
AllocTransfer dd ?
; Allocate and initialize one stage of a transfer.
; ebx -> usb_pipe, other parameters are passed through the stack:
; buffer,size = data to transfer
; flags = same as in usb_open_pipe:
; bit 0 = allow short transfer, other bits reserved
; td = pointer to the current end-of-queue descriptor
; direction =
; 0000b for normal transfers,
; 1000b for control SETUP transfer,
; 1101b for control OUT transfer,
; 1110b for control IN transfer
; returns eax = pointer to the new end-of-queue descriptor
; (not included in the queue itself) or 0 on error
InsertTransfer dd ?
; Activate previously initialized transfer (maybe with multiple stages).
; esi -> usb_controller, ebx -> usb_pipe,
; [esp+4] -> first usb_gtd for the transfer,
; ecx -> last descriptor for the transfer
NewDevice dd ?
; Initiate configuration of a new device (create pseudo-pipe describing that
; device and call usb_new_device).
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
ends
; =============================================================================
; =================================== Code ====================================
; =============================================================================
; Initializes one controller, called by usb_init for every controller. ; Initializes one controller, called by usb_init for every controller.
; edi -> usb_hardware_func, eax -> PCIDEV structure for the device. ; eax -> PCIDEV structure for the device.
proc usb_init_controller proc usb_init_controller
push ebp push ebp
mov ebp, esp mov ebp, esp
@ -240,6 +35,10 @@ proc usb_init_controller
; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs. ; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs.
push dword [eax+PCIDEV.devfn] push dword [eax+PCIDEV.devfn]
push eax push eax
mov edi, [eax+PCIDEV.owner]
test edi, edi
jz .nothing
mov edi, [edi+USBSRV.usb_func]
; 2. Allocate *hci_controller + usb_controller. ; 2. Allocate *hci_controller + usb_controller.
mov ebx, [edi+usb_hardware_func.DataSize] mov ebx, [edi+usb_hardware_func.DataSize]
add ebx, sizeof.usb_controller add ebx, sizeof.usb_controller
@ -264,6 +63,8 @@ proc usb_init_controller
mov [edi+usb_controller.ResettingPort-sizeof.usb_controller], al ; no resetting port mov [edi+usb_controller.ResettingPort-sizeof.usb_controller], al ; no resetting port
dec eax ; don't allocate zero address dec eax ; don't allocate zero address
mov [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax mov [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax
mov eax, [ebp-4]
mov [edi+usb_controller.PCICoordinates-sizeof.usb_controller], eax
lea ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller] lea ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller]
call mutex_init call mutex_init
add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock
@ -474,3 +275,48 @@ proc usb_process_one_wait_list
.nothing: .nothing:
ret ret
endp endp
; Called from USB1 controller-specific initialization.
; Finds EHCI companion controller for given USB1 controller.
; in: bl = PCI device:function for USB1 controller, bh = PCI bus
; out: eax -> usb_controller for EHCI companion
proc usb_find_ehci_companion
; 1. Loop over all registered controllers.
mov eax, usb_controllers_list
.next:
mov eax, [eax+usb_controller.Next]
cmp eax, usb_controllers_list
jz .notfound
; 2. For every controller, check the type, ignore everything that is not EHCI.
mov edx, [eax+usb_controller.HardwareFunc]
cmp [edx+usb_hardware_func.ID], 'EHCI'
jnz .next
; 3. For EHCI controller, compare PCI coordinates with input data:
; bus and device must be the same, function can be different.
mov edx, [eax+usb_controller.PCICoordinates]
xor edx, ebx
cmp dx, 8
jae .next
ret
.notfound:
xor eax, eax
ret
endp
; Find Transaction Translator hub and port for the given device.
; in: edx = parent hub for the device, ecx = port for the device
; out: edx = TT hub for the device, ecx = TT port for the device.
proc usb_get_tt
; If the parent hub is high-speed, it is TT for the device.
; Otherwise, the parent hub itself is behind TT, and the device
; has the same TT hub+port as the parent hub.
mov eax, [edx+usb_hub.ConfigPipe]
mov eax, [eax+usb_pipe.DeviceData]
cmp [eax+usb_device_data.Speed], USB_SPEED_HS
jz @f
movzx ecx, [eax+usb_device_data.TTPort]
mov edx, [eax+usb_device_data.TTHub]
@@:
mov edx, [edx+usb_hub.ConfigPipe]
ret
endp

View File

@ -1262,3 +1262,14 @@ end virtual
.nothing: .nothing:
retn 4 retn 4
endp endp
; Helper function for USB2 scheduler.
; in: eax -> usb_hub
; out: ecx = TT think time for the hub in FS-bytes
proc usb_get_tt_think_time
movzx ecx, [eax+usb_hub.HubCharacteristics]
shr ecx, 5
and ecx, 3
inc ecx
ret
endp

View File

@ -29,19 +29,20 @@
; ProcessDeferred and sleeps until this moment is reached or the thread ; ProcessDeferred and sleeps until this moment is reached or the thread
; is awakened by IRQ handler. ; is awakened by IRQ handler.
iglobal
uhci_service_name:
db 'UHCI',0
ohci_service_name:
db 'OHCI',0
ehci_service_name:
db 'EHCI',0
endg
; Initializes the USB subsystem. ; Initializes the USB subsystem.
proc usb_init proc usb_init
; 1. Initialize all locks. ; 1. Initialize all locks.
mov ecx, usb_controllers_list_mutex mov ecx, usb_controllers_list_mutex
call mutex_init call mutex_init
mov ecx, usb1_ep_mutex
call mutex_init
mov ecx, usb_gtd_mutex
call mutex_init
mov ecx, ehci_ep_mutex
call mutex_init
mov ecx, ehci_gtd_mutex
call mutex_init
; 2. Kick off BIOS from all USB controllers, calling the corresponding function ; 2. Kick off BIOS from all USB controllers, calling the corresponding function
; *hci_kickoff_bios. Also count USB controllers for the next step. ; *hci_kickoff_bios. Also count USB controllers for the next step.
; Note: USB1 companion(s) must go before the corresponding EHCI controller, ; Note: USB1 companion(s) must go before the corresponding EHCI controller,
@ -59,18 +60,33 @@ proc usb_init
jz .done_kickoff jz .done_kickoff
cmp word [esi+PCIDEV.class+1], 0x0C03 cmp word [esi+PCIDEV.class+1], 0x0C03
jnz .kickoff jnz .kickoff
mov eax, uhci_kickoff_bios mov ebx, uhci_service_name
cmp byte [esi+PCIDEV.class], 0x00 cmp byte [esi+PCIDEV.class], 0x00
jz .do_kickoff jz .do_kickoff
mov eax, ohci_kickoff_bios mov ebx, ohci_service_name
cmp byte [esi+PCIDEV.class], 0x10 cmp byte [esi+PCIDEV.class], 0x10
jz .do_kickoff jz .do_kickoff
mov eax, ehci_kickoff_bios mov ebx, ehci_service_name
cmp byte [esi+PCIDEV.class], 0x20 cmp byte [esi+PCIDEV.class], 0x20
jnz .kickoff jnz .kickoff
.do_kickoff: .do_kickoff:
inc dword [esp] inc dword [esp]
call eax push ebx esi
stdcall get_service, ebx
pop esi ebx
test eax, eax
jz .driver_fail
mov edx, [eax+USBSRV.usb_func]
cmp [edx+usb_hardware_func.Version], USBHC_VERSION
jnz .driver_invalid
mov [esi+PCIDEV.owner], eax
call [edx+usb_hardware_func.BeforeInit]
jmp .kickoff
.driver_fail:
DEBUGF 1,'K : failed to load driver %s\n',ebx
jmp .kickoff
.driver_invalid:
DEBUGF 1,'K : driver %s has wrong version\n',ebx
jmp .kickoff jmp .kickoff
.done_kickoff: .done_kickoff:
pop eax pop eax
@ -97,7 +113,6 @@ proc usb_init
jz .done_ehci jz .done_ehci
cmp [eax+PCIDEV.class], 0x0C0320 cmp [eax+PCIDEV.class], 0x0C0320
jnz .scan_ehci jnz .scan_ehci
mov edi, ehci_hardware_func
call usb_init_controller call usb_init_controller
jmp .scan_ehci jmp .scan_ehci
.done_ehci: .done_ehci:
@ -108,10 +123,8 @@ proc usb_init
mov eax, [eax+PCIDEV.fd] mov eax, [eax+PCIDEV.fd]
cmp eax, pcidev_list cmp eax, pcidev_list
jz .done_usb1 jz .done_usb1
mov edi, uhci_hardware_func
cmp [eax+PCIDEV.class], 0x0C0300 cmp [eax+PCIDEV.class], 0x0C0300
jz @f jz @f
mov edi, ohci_hardware_func
cmp [eax+PCIDEV.class], 0x0C0310 cmp [eax+PCIDEV.class], 0x0C0310
jnz .scan_usb1 jnz .scan_usb1
@@: @@:
@ -238,11 +251,8 @@ usb_controllers_list_mutex MUTEX
endg endg
include "memory.inc" include "memory.inc"
include "common.inc"
include "hccommon.inc" include "hccommon.inc"
include "pipe.inc" include "pipe.inc"
include "ohci.inc"
include "uhci.inc"
include "ehci.inc"
include "protocol.inc" include "protocol.inc"
include "hub.inc" include "hub.inc"
include "scheduler.inc"

View File

@ -12,77 +12,6 @@
; Data for one pool: dd pointer to the first page, MUTEX lock. ; Data for one pool: dd pointer to the first page, MUTEX lock.
uglobal
; Structures in UHCI and OHCI have equal sizes.
; Thus, functions and data for allocating/freeing can be shared;
; we keep them here rather than in controller-specific files.
align 4
; Data for UHCI and OHCI endpoints pool.
usb1_ep_first_page dd ?
usb1_ep_mutex MUTEX
; Data for UHCI and OHCI general transfer descriptors pool.
usb_gtd_first_page dd ?
usb_gtd_mutex MUTEX
endg
; sanity check: structures in UHCI and OHCI should be the same for allocation
if (sizeof.ohci_pipe = sizeof.uhci_pipe)
; Allocates one endpoint structure for UHCI/OHCI.
; Returns pointer to software part (usb_pipe) in eax.
proc usb1_allocate_endpoint
push ebx
mov ebx, usb1_ep_mutex
stdcall usb_allocate_common, (sizeof.ohci_pipe + sizeof.usb_pipe + 0Fh) and not 0Fh
test eax, eax
jz @f
add eax, sizeof.ohci_pipe
@@:
pop ebx
ret
endp
; Free one endpoint structure for UHCI/OHCI.
; Stdcall with one argument, pointer to software part (usb_pipe).
proc usb1_free_endpoint
sub dword [esp+4], sizeof.ohci_pipe
jmp usb_free_common
endp
else
; sanity check continued
.err allocate_endpoint/free_endpoint must be different for OHCI and UHCI
end if
; sanity check: structures in UHCI and OHCI should be the same for allocation
if (sizeof.ohci_gtd = sizeof.uhci_gtd)
; Allocates one general transfer descriptor structure for UHCI/OHCI.
; Returns pointer to software part (usb_gtd) in eax.
proc usb1_allocate_general_td
push ebx
mov ebx, usb_gtd_mutex
stdcall usb_allocate_common, (sizeof.ohci_gtd + sizeof.usb_gtd + 0Fh) and not 0Fh
test eax, eax
jz @f
add eax, sizeof.ohci_gtd
@@:
pop ebx
ret
endp
; Free one general transfer descriptor structure for UHCI/OHCI.
; Stdcall with one argument, pointer to software part (usb_gtd).
proc usb1_free_general_td
sub dword [esp+4], sizeof.ohci_gtd
jmp usb_free_common
endp
else
; sanity check continued
.err allocate_general_td/free_general_td must be different for OHCI and UHCI
end if
; Allocator for fixed-size blocks: allocate a block. ; Allocator for fixed-size blocks: allocate a block.
; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure. ; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
proc usb_allocate_common proc usb_allocate_common
@ -187,12 +116,12 @@ end virtual
ret 4 ret 4
endp endp
; Helper procedure for OHCI: translate physical address in ecx ; Helper procedure: translate physical address in ecx
; of some transfer descriptor to linear address. ; of some transfer descriptor to linear address.
; in: eax = address of first page
proc usb_td_to_virt proc usb_td_to_virt
; Traverse all pages used for transfer descriptors, looking for the one ; Traverse all pages used for transfer descriptors, looking for the one
; with physical address as in ecx. ; with physical address as in ecx.
mov eax, [usb_gtd_first_page]
@@: @@:
test eax, eax test eax, eax
jz .zero jz .zero

View File

@ -1,195 +1,5 @@
; Functions for USB pipe manipulation: opening/closing, sending data etc. ; Functions for USB pipe manipulation: opening/closing, sending data etc.
; ;
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; USB pipe types
CONTROL_PIPE = 0
ISOCHRONOUS_PIPE = 1
BULK_PIPE = 2
INTERRUPT_PIPE = 3
; Status codes for transfer callbacks.
; Taken from OHCI as most verbose controller in this sense.
USB_STATUS_OK = 0 ; no error
USB_STATUS_CRC = 1 ; CRC error
USB_STATUS_BITSTUFF = 2 ; bit stuffing violation
USB_STATUS_TOGGLE = 3 ; data toggle mismatch
USB_STATUS_STALL = 4 ; device returned STALL
USB_STATUS_NORESPONSE = 5 ; device not responding
USB_STATUS_PIDCHECK = 6 ; invalid PID check bits
USB_STATUS_WRONGPID = 7 ; unexpected PID value
USB_STATUS_OVERRUN = 8 ; too many data from endpoint
USB_STATUS_UNDERRUN = 9 ; too few data from endpoint
USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer
USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
USB_STATUS_CLOSED = 16 ; pipe closed
; either explicitly with USBClosePipe
; or implicitly due to device disconnect
; flags for usb_pipe.Flags
USB_FLAG_CLOSED = 1 ; pipe is closed, no new transfers
; pipe is closed, return error instead of submitting any new transfer
USB_FLAG_CAN_FREE = 2
; pipe is closed via explicit call to USBClosePipe, so it can be freed without
; any driver notification; if this flag is not set, then the pipe is closed due
; to device disconnect, so it must remain valid until return from disconnect
; callback provided by the driver
USB_FLAG_EXTRA_WAIT = 4
; The pipe was in wait list, while another event occured;
; when the first wait will be done, reinsert the pipe to wait list
USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT
; =============================================================================
; ================================ Structures =================================
; =============================================================================
; Pipe descriptor.
; * An USB pipe is described by two structures, for hardware and for software.
; * This is the software part. The hardware part is defined in a driver
; of the corresponding controller.
; * The hardware part is located immediately before usb_pipe,
; both are allocated at once by controller-specific code
; (it knows the total length, which depends on the hardware part).
struct usb_pipe
Controller dd ?
; Pointer to usb_controller structure corresponding to this pipe.
; Must be the first dword after hardware part, see *hci_new_device.
;
; Every endpoint is included into one of processing lists:
; * Bulk list contains all Bulk endpoints.
; * Control list contains all Control endpoints.
; * Several Periodic lists serve Interrupt endpoints with different interval.
; - There are N=2^n "leaf" periodic lists for N ms interval, one is processed
; in the frames 0,N,2N,..., another is processed in the frames
; 1,1+N,1+2N,... and so on. The hardware starts processing of periodic
; endpoints in every frame from the list identified by lower n bits of the
; frame number; the addresses of these N lists are written to the
; controller data area during the initialization.
; - We assume that n=5, N=32 to simplify the code and compact the data.
; OHCI works in this way. UHCI and EHCI actually have n=10, N=1024,
; but this is an overkill for interrupt endpoints; the large value of N is
; useful only for isochronous transfers in UHCI and EHCI. UHCI/EHCI code
; initializes "leaf" lists k,k+32,k+64,...,k+(1024-32) to the same value,
; giving essentially N=32.
; This restriction means that the actual maximum interval of polling any
; interrupt endpoint is 32ms, which seems to be a reasonable value.
; - Similarly, there are 16 lists for 16-ms interval, 8 lists for 8-ms
; interval and so on. Finally, there is one list for 1ms interval. Their
; addresses are not directly known to the controller.
; - The hardware serves endpoints following a physical link from the hardware
; part.
; - The hardware links are organized as follows. If the list item is not the
; last, it's hardware link points to the next item. The hardware link of
; the last item points to the first item of the "next" list.
; - The "next" list for k-th and (k+M)-th periodic lists for interval 2M ms
; is the k-th periodic list for interval M ms, M >= 1. In this scheme,
; if two "previous" lists are served in the frames k,k+2M,k+4M,...
; and k+M,k+3M,k+5M,... correspondingly, the "next" list is served in
; the frames k,k+M,k+2M,k+3M,k+4M,k+5M,..., which is exactly what we want.
; - The links between Periodic, Control, Bulk lists and the processing of
; Isochronous endpoints are controller-specific.
; * The head of every processing list is a static entry which does not
; correspond to any real pipe. It is described by usb_static_ep
; structure, not usb_pipe. For OHCI and UHCI, sizeof.usb_static_ep plus
; sizeof hardware part is 20h, the total number of lists is
; 32+16+8+4+2+1+1+1 = 65, so all these structures fit in one page,
; leaving space for other data. This is another reason for 32ms limit.
; * Static endpoint descriptors are kept in *hci_controller structure.
; * All items in every processing list, including the static head, are
; organized in a double-linked list using .NextVirt and .PrevVirt fields.
; * [[item.NextVirt].PrevVirt] = [[item.PrevVirt].NextVirt] for all items.
NextVirt dd ?
; Next endpoint in the processing list.
; See also PrevVirt field and the description before NextVirt field.
PrevVirt dd ?
; Previous endpoint in the processing list.
; See also NextVirt field and the description before NextVirt field.
;
; Every pipe has the associated transfer queue, that is, the double-linked
; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt
; endpoints this list consists of usb_gtd structures
; (GTD = General Transfer Descriptors), for Isochronous endpoints
; this list consists of usb_itd structures, which are not developed yet.
; The pipe needs to know only the last TD; the first TD can be
; obtained as [[pipe.LastTD].NextVirt].
LastTD dd ?
; Last TD in the transfer queue.
;
; All opened pipes corresponding to the same physical device are organized in
; the double-linked list using .NextSibling and .PrevSibling fields.
; The head of this list is kept in usb_device_data structure (OpenedPipeList).
; This list is used when the device is disconnected and all pipes for the
; device should be closed.
; Also, all pipes closed due to disconnect must remain valid at least until
; driver-provided disconnect function returns; all should-be-freed-but-not-now
; pipes for one device are organized in another double-linked list with
; the head in usb_device_data.ClosedPipeList; this list uses the same link
; fields, one pipe can never be in both lists.
NextSibling dd ?
; Next pipe for the physical device.
PrevSibling dd ?
; Previous pipe for the physical device.
;
; When hardware part of pipe is changed, some time is needed before further
; actions so that hardware reacts on this change. During that time,
; all changed pipes are organized in single-linked list with the head
; usb_controller.WaitPipeList* and link field NextWait.
; Currently there are two possible reasons to change:
; change of address/packet size in initial configuration,
; close of the pipe. They are distinguished by USB_FLAG_CLOSED.
NextWait dd ?
Lock MUTEX
; Mutex that guards operations with transfer queue for this pipe.
Type db ?
; Type of pipe, one of {CONTROL,ISOCHRONOUS,BULK,INTERRUPT}_PIPE.
Flags db ?
; Combination of flags, USB_FLAG_*.
rb 2 ; dword alignment
DeviceData dd ?
; Pointer to usb_device_data, common for all pipes for one device.
ends
; This structure describes the static head of every list of pipes.
struct usb_static_ep
; software fields
Bandwidth dd ?
; valid only for interrupt/isochronous USB1 lists
; The offsets of the following two fields must be the same in this structure
; and in usb_pipe.
NextVirt dd ?
PrevVirt dd ?
ends
; This structure represents one transfer descriptor
; ('g' stands for "general" as opposed to isochronous usb_itd).
; Note that one transfer can have several descriptors:
; a control transfer has three stages.
; Additionally, every controller has a limit on transfer length with
; one descriptor (packet size for UHCI, 1K for OHCI, 4K for EHCI),
; large transfers must be split into individual packets according to that limit.
struct usb_gtd
Callback dd ?
; Zero for intermediate descriptors, pointer to callback function
; for final descriptor. See the docs for description of the callback.
UserData dd ?
; Dword which is passed to Callback as is, not used by USB code itself.
; Two following fields organize all descriptors for one pipe in
; the linked list.
NextVirt dd ?
PrevVirt dd ?
Pipe dd ?
; Pointer to the parent usb_pipe.
Buffer dd ?
; Pointer to data for this descriptor.
Length dd ?
; Length of data for this descriptor.
ends
; =============================================================================
; =================================== Code ====================================
; =============================================================================
USB_STDCALL_VERIFY = 1 USB_STDCALL_VERIFY = 1
macro stdcall_verify [arg] macro stdcall_verify [arg]
{ {
@ -216,7 +26,7 @@ endp
proc usb_open_pipe stdcall uses ebx esi edi,\ proc usb_open_pipe stdcall uses ebx esi edi,\
config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword
locals locals
tt_vars rd (ehci_select_tt_interrupt_list.local_vars_size + 3) / 4 tt_vars rd 24 ; should be enough for ehci_select_tt_interrupt_list
targetsmask dd ? ; S-Mask for USB2 targetsmask dd ? ; S-Mask for USB2
bandwidth dd ? bandwidth dd ?
target dd ? target dd ?
@ -810,6 +620,27 @@ proc usb_unlink_td
ret ret
endp endp
; One part of transfer is completed, run the associated callback
; or update total length in the next part of transfer.
; in: ebx -> usb_gtd, ecx = status, edx = length
proc usb_process_gtd
; 1. Test whether it is the last descriptor in the transfer
; <=> it has an associated callback.
mov eax, [ebx+usb_gtd.Callback]
test eax, eax
jz .nocallback
; 2. It has an associated callback; call it with corresponding parameters.
stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
ret
.nocallback:
; 3. It is an intermediate descriptor. Add its length to the length
; in the following descriptor.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
ret
endp
if USB_STDCALL_VERIFY if USB_STDCALL_VERIFY
proc verify_regs proc verify_regs
virtual at esp virtual at esp

View File

@ -29,11 +29,6 @@ USB_DEVICE_QUALIFIER_DESCR = 6
USB_OTHER_SPEED_CONFIG_DESCR = 7 USB_OTHER_SPEED_CONFIG_DESCR = 7
USB_INTERFACE_POWER_DESCR = 8 USB_INTERFACE_POWER_DESCR = 8
; Possible speeds of USB devices
USB_SPEED_FS = 0 ; full-speed
USB_SPEED_LS = 1 ; low-speed
USB_SPEED_HS = 2 ; high-speed
; Compile-time setting. If set, the code will dump all descriptors as they are ; Compile-time setting. If set, the code will dump all descriptors as they are
; read to the debug board. ; read to the debug board.
USB_DUMP_DESCRIPTORS = 1 USB_DUMP_DESCRIPTORS = 1
@ -370,7 +365,7 @@ proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, l
; so setting the address could take some time until the cache is evicted. ; so setting the address could take some time until the cache is evicted.
; Thus, the call is asynchronous; meet us in usb_after_set_address when it will ; Thus, the call is asynchronous; meet us in usb_after_set_address when it will
; be safe to continue. ; be safe to continue.
dbgstr 'address set in device' ; dbgstr 'address set in device'
call [eax+usb_hardware_func.SetDeviceAddress] call [eax+usb_hardware_func.SetDeviceAddress]
; 2b. If the port is in non-root hub, clear 'reset in progress' flag. ; 2b. If the port is in non-root hub, clear 'reset in progress' flag.
; In any case, proceed to 4. ; In any case, proceed to 4.
@ -409,7 +404,7 @@ endp
; is cleared after request from usb_set_address_callback. ; is cleared after request from usb_set_address_callback.
; in: ebx -> usb_pipe ; in: ebx -> usb_pipe
proc usb_after_set_address proc usb_after_set_address
dbgstr 'address set for controller' ; dbgstr 'address set for controller'
; Issue control transfer GET_DESCRIPTOR(DEVICE_DESCR) for first 8 bytes. ; Issue control transfer GET_DESCRIPTOR(DEVICE_DESCR) for first 8 bytes.
; Remember, we still do not know the actual packet size; ; Remember, we still do not know the actual packet size;
; 8-bytes-request is safe. ; 8-bytes-request is safe.

View File

@ -509,6 +509,8 @@ struct PCIDEV
class dd ? class dd ?
devfn db ? devfn db ?
bus db ? bus db ?
rb 2
owner dd ? ; pointer to SRV or 0
ends ends
; The following macro assume that we are on uniprocessor machine. ; The following macro assume that we are on uniprocessor machine.

View File

@ -214,10 +214,35 @@ proc get_service stdcall, sz_name:dword
mov edx, [edx+SRV.fd] mov edx, [edx+SRV.fd]
jmp @B jmp @B
.not_load: .not_load:
mov eax, [sz_name]
; Try to load .dll driver first. If not, fallback to .obj.
push edi
sub esp, 36
mov edi, esp
mov dword [edi], '/sys'
mov dword [edi+4], '/dri'
mov dword [edi+8], 'vers'
mov byte [edi+12], '/'
@@:
mov dl, [eax]
mov [edi+13], dl
inc eax
inc edi
test dl, dl
jnz @b
mov dword [edi+12], '.sys'
mov byte [edi+16], 0
mov edi, esp
stdcall load_pe_driver, edi, 0
add esp, 36
pop edi
test eax, eax
jnz .nothing
pop ebp pop ebp
jmp load_driver jmp load_driver
.ok: .ok:
mov eax, edx mov eax, edx
.nothing:
ret ret
endp endp

View File

@ -45,6 +45,7 @@ __exports:
map_io_mem, 'MapIoMem', \ ; stdcall map_io_mem, 'MapIoMem', \ ; stdcall
map_page, 'MapPage', \ ; stdcall map_page, 'MapPage', \ ; stdcall
get_pg_addr, 'GetPgAddr', \ ; eax get_pg_addr, 'GetPgAddr', \ ; eax
get_phys_addr, 'GetPhysAddr', \ ; eax
map_space, 'MapSpace', \ map_space, 'MapSpace', \
release_pages, 'ReleasePages', \ release_pages, 'ReleasePages', \
\ \
@ -113,6 +114,7 @@ __exports:
usb_normal_transfer_async, 'USBNormalTransferAsync', \ usb_normal_transfer_async, 'USBNormalTransferAsync', \
usb_control_async, 'USBControlTransferAsync', \ usb_control_async, 'USBControlTransferAsync', \
usb_get_param, 'USBGetParam', \ usb_get_param, 'USBGetParam', \
usb_hc_func, 'USBHCFunc', \
\ \
NET_add_device, 'NetRegDev', \ NET_add_device, 'NetRegDev', \
NET_remove_device, 'NetUnRegDev', \ NET_remove_device, 'NetUnRegDev', \

View File

@ -1253,22 +1253,7 @@ f68:
cmp edx, OS_BASE cmp edx, OS_BASE
jae .fail jae .fail
mov edi, edx stdcall load_pe_driver, ecx, edx
stdcall load_PE, ecx
mov esi, eax
test eax, eax
jz @F
push edi
push DRV_ENTRY
call eax
add esp, 8
test eax, eax
jz @F
mov [eax+SRV.entry], esi
@@:
mov [esp+32], eax mov [esp+32], eax
ret ret
.22: .22:
@ -1348,26 +1333,32 @@ f68call: ; keep this table closer to main code
align 4 align 4
proc load_pe_driver stdcall, file:dword proc load_pe_driver stdcall, file:dword, cmdline:dword
push esi
stdcall load_PE, [file] stdcall load_PE, [file]
test eax, eax test eax, eax
jz .fail jz .fail
mov esi, eax mov esi, eax
stdcall eax, DRV_ENTRY push [cmdline]
push DRV_ENTRY
call eax
pop ecx
pop ecx
test eax, eax test eax, eax
jz .fail jz .fail
mov [eax+SRV.entry], esi mov [eax+SRV.entry], esi
pop esi
ret ret
.fail: .fail:
xor eax, eax xor eax, eax
pop esi
ret ret
endp endp
align 4 align 4
proc init_mtrr proc init_mtrr
@ -1415,9 +1406,9 @@ proc init_mtrr
xor eax, eax xor eax, eax
xor edx, edx xor edx, edx
@@: @@:
wrmsr
inc ecx inc ecx
cmp ecx, 0x210 wrmsr
cmp ecx, 0x20F
jb @b jb @b
; enable MTRRs ; enable MTRRs
pop eax pop eax

View File

@ -85,13 +85,12 @@ L3:
mov ecx, eax mov ecx, eax
add edi, DWORD PTR [edx+260] add edi, DWORD PTR [edx+260]
add ecx, 3
shr ecx, 2 shr ecx, 2
rep movsd rep movsd
L4: L4:
mov ecx, DWORD PTR [edx+256] mov ecx, DWORD PTR [edx+256]
add ecx, 4095
and ecx, -4096
cmp ecx, eax cmp ecx, eax
jbe L6 jbe L6
sub ecx, eax sub ecx, eax
@ -178,6 +177,10 @@ L23:
mov ecx, DWORD PTR [eax-4] mov ecx, DWORD PTR [eax-4]
mov DWORD PTR [esp+48], edi mov DWORD PTR [esp+48], edi
mov edx, DWORD PTR [eax-20] mov edx, DWORD PTR [eax-20]
test edx, edx
jnz @f
mov edx, ecx
@@:
mov DWORD PTR [esp+52], 0 mov DWORD PTR [esp+52], 0
add ecx, ebp add ecx, ebp
add edx, ebp add edx, ebp