From a10422fbce99a7f5a16a19cfaa3065fea9b07237 Mon Sep 17 00:00:00 2001 From: CleverMouse Date: Mon, 30 Dec 2013 11:18:33 +0000 Subject: [PATCH] separate USB host controller code into external drivers git-svn-id: svn://kolibrios.org@4418 a494cfbc-eb01-0410-851d-a64ba20cac60 --- data/eng/Makefile | 3 + data/et/Makefile | 3 + data/it/Makefile | 3 + data/rus/Makefile | 3 + data/sp/Makefile | 3 + drivers/fdo.inc | 16 +- .../bus/usb/ehci.inc => drivers/usb/ehci.asm | 343 ++++++++++-------- .../usb/ehci_scheduler.inc | 238 +----------- .../bus/usb/ohci.inc => drivers/usb/ohci.asm | 299 +++++++++------ .../bus/usb/uhci.inc => drivers/usb/uhci.asm | 331 ++++++++++------- drivers/usb/usb1_scheduler.inc | 232 ++++++++++++ kernel/trunk/bus/pci/pci32.inc | 3 +- kernel/trunk/bus/usb/hccommon.inc | 306 ++++------------ kernel/trunk/bus/usb/hub.inc | 11 + kernel/trunk/bus/usb/init.inc | 48 ++- kernel/trunk/bus/usb/memory.inc | 75 +--- kernel/trunk/bus/usb/pipe.inc | 213 ++--------- kernel/trunk/bus/usb/protocol.inc | 9 +- kernel/trunk/const.inc | 2 + kernel/trunk/core/dll.inc | 25 ++ kernel/trunk/core/exports.inc | 2 + kernel/trunk/core/memory.inc | 33 +- kernel/trunk/core/peload.inc | 7 +- 23 files changed, 1037 insertions(+), 1171 deletions(-) rename kernel/trunk/bus/usb/ehci.inc => drivers/usb/ehci.asm (90%) rename kernel/trunk/bus/usb/scheduler.inc => drivers/usb/ehci_scheduler.inc (77%) rename kernel/trunk/bus/usb/ohci.inc => drivers/usb/ohci.asm (90%) rename kernel/trunk/bus/usb/uhci.inc => drivers/usb/uhci.asm (91%) create mode 100644 drivers/usb/usb1_scheduler.inc diff --git a/data/eng/Makefile b/data/eng/Makefile index ab5347a560..c8445156c3 100644 --- a/data/eng/Makefile +++ b/data/eng/Makefile @@ -147,6 +147,9 @@ FASM_PROGRAMS:=\ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.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/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ diff --git a/data/et/Makefile b/data/et/Makefile index b1851968d4..5e6e275131 100644 --- a/data/et/Makefile +++ b/data/et/Makefile @@ -147,6 +147,9 @@ FASM_PROGRAMS:=\ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.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/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ diff --git a/data/it/Makefile b/data/it/Makefile index 9afab5f7c8..479d1186ec 100644 --- a/data/it/Makefile +++ b/data/it/Makefile @@ -147,6 +147,9 @@ FASM_PROGRAMS:=\ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.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/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ diff --git a/data/rus/Makefile b/data/rus/Makefile index 69e3612389..5cccdc4743 100644 --- a/data/rus/Makefile +++ b/data/rus/Makefile @@ -148,6 +148,9 @@ FASM_PROGRAMS:=\ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.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/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ diff --git a/data/sp/Makefile b/data/sp/Makefile index 0fde642a9d..f60f53c2b1 100644 --- a/data/sp/Makefile +++ b/data/sp/Makefile @@ -147,6 +147,9 @@ FASM_PROGRAMS:=\ drivers/rtl8139.obj:DRIVERS/RTL8139.obj:$(REPOSITORY)/drivers/ethernet/RTL8139.asm \ drivers/rtl8169.obj:DRIVERS/RTL8169.obj:$(REPOSITORY)/drivers/ethernet/RTL8169.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/kfm:File|Managers/KFM:$(PROGS)/fs/kfm/trunk/kfm.asm \ File|Managers/opendial:File|Managers/OPENDIAL:$(PROGS)/fs/opendial/opendial.asm \ diff --git a/drivers/fdo.inc b/drivers/fdo.inc index ff782b6bf9..15031d6cd8 100644 --- a/drivers/fdo.inc +++ b/drivers/fdo.inc @@ -236,9 +236,11 @@ debug_beginf pushad movzx ecx,al mov ebx,1 -; mov ecx,sys_msg_board -; call ecx ; sys_msg_board - stdcall SysMsgBoard +if defined SysMsgBoard._pe_import + invoke SysMsgBoard +else + stdcall SysMsgBoard +end if popad ret debug_endf @@ -251,9 +253,11 @@ debug_beginf movzx ecx,byte[edx] or cl,cl jz .l2 -; mov ecx,sys_msg_board -; call ecx ; sys_msg_board - stdcall SysMsgBoard +if defined SysMsgBoard._pe_import + invoke SysMsgBoard +else + stdcall SysMsgBoard +end if inc edx jmp .l1 .l2: ret diff --git a/kernel/trunk/bus/usb/ehci.inc b/drivers/usb/ehci.asm similarity index 90% rename from kernel/trunk/bus/usb/ehci.inc rename to drivers/usb/ehci.asm index 2da2d5d8ea..81a111ab7f 100644 --- a/kernel/trunk/bus/usb/ehci.inc +++ b/drivers/usb/ehci.asm @@ -1,7 +1,17 @@ ; 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 -; development, but there are no reasons to keep it here. + +; Standard driver stuff +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 ================================= @@ -246,6 +256,11 @@ CapabilityParams dd ? DeferredActions dd ? ; Bitmask of events from EhciStatusReg which were observed by the IRQ handler ; 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 if ehci_controller.IntEDs mod 32 @@ -258,8 +273,10 @@ end if iglobal align 4 ehci_hardware_func: + dd USBHC_VERSION dd 'EHCI' dd sizeof.ehci_controller + dd ehci_kickoff_bios dd ehci_init dd ehci_process_deferred dd ehci_set_device_address @@ -276,12 +293,39 @@ ehci_hardware_func: dd ehci_alloc_transfer dd ehci_insert_transfer dd ehci_new_device +ehci_name db 'EHCI',0 endg ; ============================================================================= ; =================================== 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. ; Called from usb_init_controller. Initializes the hardware and ; EHCI-specific parts of software structures. @@ -313,7 +357,7 @@ if (ehci_controller.IntEDs mod 0x1000) <> 0 .err assertion failed end if add eax, ehci_controller.IntEDs - call get_phys_addr + call [GetPhysAddr] ; 2b. Fill first 32 entries. inc eax inc eax ; set Type to EHCI_TYPE_QH @@ -372,24 +416,15 @@ end if call ehci_init_static_endpoint ; 4. Create a virtual memory area to talk with the controller. ; 4a. Enable memory & bus master access. - mov ch, [.bus] - mov cl, 1 - mov eax, ecx - mov bh, [.devfn] - mov bl, 4 - call pci_read_reg + invoke PciRead16, dword [.bus], dword [.devfn], 4 or al, 6 - xchg eax, ecx - call pci_write_reg + invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax ; 4b. Read memory base address. - mov ah, [.bus] - mov al, 2 - mov bl, 10h - call pci_read_reg + invoke PciRead32, dword [.bus], dword [.devfn], 10h ; DEBUGF 1,'K : phys MMIO %x\n',eax and al, not 0Fh ; 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 jz .fail ; 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 end if 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] mov edx, [eax+EhciCapParamsReg] - mov ebx, [eax+EhciStructParamsReg] add eax, ecx if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4 .err assertion failed @@ -426,7 +523,7 @@ end if and dword [edi+EhciCommandReg], not 1 @@: movi esi, 1 - call delay_ms + invoke Sleep test dword [edi+EhciStatusReg], 1 shl 12 jnz .stopped loop @b @@ -438,7 +535,7 @@ end if movi ecx, 50 @@: movi esi, 1 - call delay_ms + invoke Sleep test dword [edi+EhciCommandReg], 2 jz .reset_ok loop @b @@ -455,25 +552,21 @@ end if mov dword [edi+EhciCtrlDataSegReg], 0 @@: ; 7b. Hook interrupt and enable appropriate interrupt sources. - mov ah, [.bus] - mov al, 0 - mov bh, [.devfn] - mov bl, 3Ch - call pci_read_reg + invoke PciRead8, dword [.bus], dword [.devfn], 3Ch ; al = IRQ - DEBUGF 1,'K : attaching to IRQ %x\n',al +; DEBUGF 1,'K : attaching to IRQ %x\n',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 ; disable Frame List Rollover interrupt, enable all other sources mov dword [edi+EhciInterruptReg], 110111b ; 7c. Inform the controller of the address of periodic lists head. lea eax, [esi-sizeof.ehci_controller] - call get_phys_addr + invoke GetPhysAddr mov dword [edi+EhciPeriodicListReg], eax ; 7d. Inform the controller of the address of asynchronous lists head. lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller] - call get_phys_addr + invoke GetPhysAddr mov dword [edi+EhciAsyncListReg], eax ; 7e. Configure operational details and run the controller. mov dword [edi+EhciCommandReg], \ @@ -498,7 +591,7 @@ end if jz @f push esi movi esi, 20 - call delay_ms + invoke Sleep pop esi @@: ; 9. Return pointer to usb_controller. @@ -510,7 +603,7 @@ end if .fail_unmap: pop eax push eax - stdcall free_kernel_space, [eax+ehci_controller.MMIOBase1] + invoke FreeKernelSpace, [eax+ehci_controller.MMIOBase1] .fail: pop ecx xor eax, eax @@ -529,7 +622,7 @@ proc ehci_init_static_endpoint test esi, esi jz @f mov eax, esi - call get_phys_addr + invoke GetPhysAddr inc eax 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 byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted 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 ret endp @@ -565,14 +659,10 @@ endp ; deal with USB devices. proc ehci_kickoff_bios ; 1. Get the physical address of MMIO registers. - mov ah, [esi+PCIDEV.bus] - mov bh, [esi+PCIDEV.devfn] - mov al, 2 - mov bl, 10h - call pci_read_reg + invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 10h and al, not 0Fh ; 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 jz .nothing push eax ; push argument for step 8 @@ -604,10 +694,7 @@ proc ehci_kickoff_bios test bl, 3 jnz .no_capability ; In each iteration, read the current dword, - mov ah, [esi+PCIDEV.bus] - mov al, 2 - mov bh, [esi+PCIDEV.devfn] - call pci_read_reg + invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx ; check, whether the capability ID is take-ownership ID = 1, cmp al, 1 jz .found_bios_handoff @@ -632,16 +719,11 @@ proc ehci_kickoff_bios jz .has_ownership ; 4c. Request ownership. inc ebx - mov cl, 1 - mov ah, [esi+PCIDEV.bus] - mov al, 0 - call pci_write_reg + invoke PciWrite8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, 1 ; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership ; requests; if so, there is no sense in waiting. inc ebx - mov ah, [esi+PCIDEV.bus] - mov al, 2 - call pci_read_reg + invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx dec ebx dec ebx test ah, 20h @@ -650,14 +732,12 @@ proc ehci_kickoff_bios ; If successful, go to 5. mov dword [esp], 1000 @@: - mov ah, [esi+PCIDEV.bus] - mov al, 0 - call pci_read_reg + invoke PciRead8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx test al, 1 jz .has_ownership push esi movi esi, 1 - call delay_ms + invoke Sleep pop esi dec dword [esp] jnz @b @@ -665,22 +745,15 @@ proc ehci_kickoff_bios .force_ownership: ; 4f. BIOS has not responded within the timeout. ; Let's just clear BIOS ownership flag and hope that everything will be ok. - mov ah, [esi+PCIDEV.bus] - mov al, 0 - mov cl, 0 - call pci_write_reg + invoke PciWrite8, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, 0 .has_ownership: ; 5. Just in case clear all SMI event sources except change-ownership. dbgstr 'has_ownership' inc ebx inc ebx - mov ah, [esi+PCIDEV.bus] - mov al, 2 - mov ecx, eax - call pci_read_reg + invoke PciRead16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx and ax, 2000h - xchg eax, ecx - call pci_write_reg + invoke PciWrite16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], ebx, eax .has_ownership2: pop ecx ; 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. popf ; 8. Release memory mapping created in step 2 and return. - call free_kernel_space + invoke FreeKernelSpace .nothing: ret endp @@ -717,7 +790,6 @@ end virtual mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] spin_lock_irqsave [esi+usb_controller.WaitSpinlock] 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. ; (One IRQ can be shared by several devices.) and eax, [edi+EhciInterruptReg] @@ -739,7 +811,7 @@ end virtual or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] inc ebx - call usb_wakeup_if_needed + invoke usbhc_api.usb_wakeup_if_needed ; 6. Interrupt processed; return non-zero. mov al, 1 pop edi esi ebx ; restore used registers to be cdecl @@ -751,8 +823,7 @@ endp ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address proc ehci_set_device_address mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl - call usb_subscribe_control - ret + jmp [usbhc_api.usb_subscribe_control] endp ; This procedure returns USB device address from the ehci_pipe structure. @@ -785,8 +856,7 @@ proc ehci_set_endpoint_packet_size or eax, ecx mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax ; Wait until hardware cache is evicted. - call usb_subscribe_control - ret + jmp [usbhc_api.usb_subscribe_control] endp uglobal @@ -804,7 +874,7 @@ endg proc ehci_alloc_pipe push ebx 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 jz @f add eax, sizeof.ehci_pipe @@ -821,7 +891,7 @@ virtual at esp .ptr dd ? end virtual sub [.ptr], sizeof.ehci_pipe - jmp usb_free_common + jmp [usbhc_api.usb_free_common] endp ; 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 ; 3. Store physical address of the first TD. sub eax, sizeof.ehci_gtd - call get_phys_addr + call [GetPhysAddr] mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax ; 4. Fill ehci_pipe.Flags except for S- and C-masks. ; Copy location from the config pipe. @@ -945,7 +1015,7 @@ end virtual mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx lea eax, [edi-sizeof.ehci_pipe] - call get_phys_addr + call [GetPhysAddr] inc eax inc eax mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax @@ -963,7 +1033,7 @@ endp proc ehci_new_port ; 1. If the device operates at low-speed, just release it to a companion. 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 and ah, 0Ch cmp ah, 4 @@ -996,17 +1066,17 @@ proc ehci_new_port and al, not (4 or 2Ah) mov [edi+EhciPortsReg+ecx*4], eax ; 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.ResettingStatus], 1 ; 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 .nothing: ret .low_speed: ; 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 and dl, not 2Ah mov [edi+EhciPortsReg+ecx*4], edx @@ -1051,7 +1121,8 @@ endl ; allocate full descriptors (of maximal possible size). ; 2a. Calculate size of one descriptor: must be a multiple of transfer size ; and must be not greater than 4001h. - movzx ecx, word [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe] + movzx ecx, word [ebx+ehci_pipe.Token+2-sizeof.ehci_pipe] + and ecx, (1 shl 11) - 1 mov eax, 4001h xor edx, edx mov edi, eax @@ -1092,7 +1163,7 @@ endl mov eax, [ebx+usb_pipe.Controller] add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller @@: - call get_phys_addr + call [GetPhysAddr] mov edx, [origTD] @@: cmp edx, [esp] @@ -1106,7 +1177,7 @@ endl .fail: mov edi, ehci_hardware_func mov eax, [td] - stdcall usb_undo_tds, [origTD] + invoke usbhc_api.usb_undo_tds, [origTD] xor eax, eax ret endp @@ -1134,7 +1205,7 @@ end virtual jz .nothing ; 2. Initialize controller-independent parts of both TDs. push eax - call usb_init_transfer + invoke usbhc_api.usb_init_transfer pop eax ; 3. Copy PID to the new descriptor. mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] @@ -1145,7 +1216,7 @@ end virtual push eax ; 5. Store the physical address of the next descriptor. sub eax, sizeof.ehci_gtd - call get_phys_addr + call [GetPhysAddr] mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax ; 6. For zero-length transfers, store zero in all fields for buffer addresses. ; Otherwise, fill them with real values. @@ -1157,7 +1228,7 @@ end repeat cmp [.packetSize], eax jz @f mov eax, [.buffer] - call get_phys_addr + call [GetPhysAddr] mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax and eax, 0xFFF mov edx, [.packetSize] @@ -1166,25 +1237,25 @@ end repeat jbe @f mov eax, [.buffer] add eax, 0x1000 - call get_pg_addr + call [GetPgAddr] mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax sub edx, 0x1000 jbe @f mov eax, [.buffer] add eax, 0x2000 - call get_pg_addr + call [GetPgAddr] mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax sub edx, 0x1000 jbe @f mov eax, [.buffer] add eax, 0x3000 - call get_pg_addr + call [GetPgAddr] mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax sub edx, 0x1000 jbe @f mov eax, [.buffer] add eax, 0x4000 - call get_pg_addr + call [GetPgAddr] mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax @@: ; 7. Fill Token field: @@ -1242,10 +1313,10 @@ endp proc ehci_port_reset_done movzx ecx, [esi+usb_controller.ResettingPort] and dword [edi+EhciPortsReg+ecx*4], not 12Ah - mov eax, [timer_ticks] + invoke GetTimerTicks mov [esi+usb_controller.ResetTime], eax 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 endp @@ -1258,21 +1329,23 @@ proc ehci_port_init xor eax, eax xchg al, [esi+usb_controller.ResettingStatus] 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, ; full-speed devices are left disabled; ; if the port is disabled, release it to a companion and continue to ; next device (if there is one). movzx ecx, [esi+usb_controller.ResettingPort] 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 jnz @f ; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax dbgstr 'releasing to companion' or ah, 20h 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 ; 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. .disable_exit: and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah) - jmp usb_test_pending_port + jmp [usbhc_api.usb_test_pending_port] .nothing: ret endp @@ -1307,20 +1380,10 @@ proc ehci_new_device ; of the last high-speed hub (the closest to the device hub) ; for split transactions, and set ControlEndpoint bit in eax; ; ehci_init_pipe assumes that the parent pipe is a control pipe. + push eax movzx ecx, [esi+usb_controller.ResettingPort] mov edx, [esi+usb_controller.ResettingHub] -; 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. - 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] + invoke usbhc_api.usb_get_tt inc ecx mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] shl ecx, 23 @@ -1338,7 +1401,7 @@ proc ehci_new_device push edx ; ehci_pipe.Flags push eax ; ehci_pipe.Token ; 6. Notify the protocol layer. - call usb_new_device + invoke usbhc_api.usb_new_device ; 7. Cleanup the stack after step 5 and return. add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 pop ecx ebx ; restore used registers @@ -1378,7 +1441,7 @@ proc ehci_process_deferred mov [edi+EhciPortsReg+ecx*4], eax mov ebx, eax 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. ; Note: that needs work. test bl, 20h ; overcurrent change @@ -1411,7 +1474,7 @@ proc ehci_process_deferred ; USB_CONNECT_DELAY ticks. test al, 1 jz .disconnect - mov eax, [timer_ticks] + invoke GetTimerTicks mov [esi+usb_controller.ConnectedTime+ecx*4], eax bts [esi+usb_controller.NewConnected], ecx jmp .nextport @@ -1435,7 +1498,7 @@ proc ehci_process_deferred .skip_roothub: ; 4. Process disconnect events. This should be done after step 3 ; (which includes the first stage of disconnect processing). - call usb_disconnect_stage2 + invoke usbhc_api.usb_disconnect_stage2 ; 5. Check for previously connected devices. ; 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. @@ -1447,7 +1510,7 @@ proc ehci_process_deferred .portloop2: bt [esi+usb_controller.NewConnected], ecx jnc .noconnect - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+usb_controller.ConnectedTime+ecx*4] sub eax, USB_CONNECT_DELAY jge .connected @@ -1488,7 +1551,7 @@ proc ehci_process_deferred ; Satisfy a request when InterruptOnAsyncAdvance fired. test byte [esp+4], 20h jz @f - dbgstr 'async advance int' +; dbgstr 'async advance int' mov eax, [esi+usb_controller.WaitPipeRequestAsync] mov [esi+usb_controller.ReadyPipeHeadAsync], eax @@: @@ -1531,7 +1594,7 @@ proc ehci_process_deferred cmp [esi+usb_controller.ResettingStatus], 1 jnz .no_reset_in_progress ; 8b. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+usb_controller.ResetTime] sub eax, USB_RESET_TIME jge .reset_done @@ -1549,7 +1612,7 @@ proc ehci_process_deferred cmp [esi+usb_controller.ResettingStatus], 0 jz .skip_reset ; 8f. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+usb_controller.ResetTime] sub eax, USB_RESET_RECOVERY_TIME jge .reset_recovery_done @@ -1568,7 +1631,7 @@ proc ehci_process_deferred ; 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. ; 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, ; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s ; (sometimes it just does not fire). @@ -1577,7 +1640,7 @@ proc ehci_process_deferred mov edx, [esi+usb_controller.WaitPipeListAsync] mov [esi+usb_controller.WaitPipeRequestAsync], edx or dword [edi+EhciCommandReg], 1 shl 6 - dbgstr 'async advance doorbell' +; dbgstr 'async advance doorbell' cmp dword [esp], 100 jb @f 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) ; or b) an active (not yet processed by the hardware) descriptor is reached. lea ecx, [ebx+usb_pipe.Lock] - call mutex_lock + invoke MutexLock mov ebx, [ebx+usb_pipe.LastTD] push ebx mov ebx, [ebx+usb_gtd.NextVirt] @@ -1663,13 +1726,13 @@ proc ehci_process_updated_list ; Release the queue lock while processing one descriptor: ; callback function could (and often would) schedule another transfer. push ecx - call mutex_unlock + invoke MutexUnlock call ehci_process_updated_td pop ecx - call mutex_lock + invoke MutexLock jmp .tdloop .tddone: - call mutex_unlock + invoke MutexUnlock pop ebx ; End of internal loop, restore pointer to the next pipe ; 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] ;@@: ; 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. mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] lea edx, [eax+eax] @@ -1723,21 +1786,7 @@ proc ehci_process_updated_td ; 4. Either the descriptor in ebx was processed without errors, ; or all necessary error actions were taken and ebx points to the last ; related descriptor. -; 4a. 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 -; 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: + invoke usbhc_api.usb_process_gtd ; 5. Free the current descriptor and return the next one. push [ebx+usb_gtd.NextVirt] stdcall ehci_free_td, ebx @@ -1765,12 +1814,12 @@ proc ehci_process_updated_td ; for this transfer. Free and unlink non-final descriptors. ; Final descriptor will be freed in step 5. .look_final: - call usb_is_final_packet + invoke usbhc_api.usb_is_final_packet jnc .found_final push [ebx+usb_gtd.NextVirt] stdcall ehci_free_td, ebx pop ebx - call usb_unlink_td + invoke usbhc_api.usb_unlink_td jmp .look_final .found_final: ; 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] stdcall ehci_free_td, ebx pop ebx - call usb_unlink_td + invoke usbhc_api.usb_unlink_td pop ecx .normal: ; 6f. For bulk/interrupt transfers we have no choice but halt the queue, @@ -1882,7 +1931,7 @@ endp proc ehci_alloc_td push ebx 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 jz @f add eax, sizeof.ehci_gtd @@ -1896,5 +1945,15 @@ endp ; EHCI has no additional data, so just free ehci_gtd structure. proc ehci_free_td sub dword [esp+4], sizeof.ehci_gtd - jmp usb_free_common + jmp [usbhc_api.usb_free_common] endp + +include 'ehci_scheduler.inc' + +section '.data' readable writable +include '../peimport.inc' +include_debug_strings +IncludeIGlobals +IncludeUGlobals +align 4 +usbhc_api usbhc_func diff --git a/kernel/trunk/bus/usb/scheduler.inc b/drivers/usb/ehci_scheduler.inc similarity index 77% rename from kernel/trunk/bus/usb/scheduler.inc rename to drivers/usb/ehci_scheduler.inc index 4bcd7a664b..c12c59e107 100644 --- a/kernel/trunk/bus/usb/scheduler.inc +++ b/drivers/usb/ehci_scheduler.inc @@ -2,233 +2,6 @@ ; 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). - -; 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. ; 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 ? .target_delta dd ? .local_vars_size = $ - .local_vars_start +if .local_vars_size > 24*4 +err Modify stack frame size in +end if .targetsmask dd ? .bandwidth dd ? @@ -992,9 +768,7 @@ endp ; Similar to calc_usb1_bandwidth with corresponding changes. ; eax -> usb_hub with TT, ebx -> usb_pipe proc tt_calc_budget - movzx ecx, [eax+usb_hub.HubCharacteristics] - shr ecx, 5 - and ecx, 3 ; 1+ecx = TT think time in FS-bytes + invoke usbhc_api.usb_get_tt_think_time ; ecx = TT think time in FS-bytes mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] shr eax, 16 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, ; 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. - lea eax, [eax+11+ecx+1] + lea eax, [eax+11+ecx] ; 1 byte is minimal TT think time in addition to ecx. ret .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 ; and two hub delays. ; 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 endp diff --git a/kernel/trunk/bus/usb/ohci.inc b/drivers/usb/ohci.asm similarity index 90% rename from kernel/trunk/bus/usb/ohci.inc rename to drivers/usb/ohci.asm index 1d6871064d..27d8f080f5 100644 --- a/kernel/trunk/bus/usb/ohci.inc +++ b/drivers/usb/ohci.asm @@ -1,7 +1,17 @@ ; 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 -; development, but there are no reasons to keep it here. + +; Standard driver stuff +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 ================================= @@ -183,6 +193,8 @@ DoneListEndPtr dd ? ; 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; ; 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 if ohci_controller.IntEDs mod 16 @@ -290,8 +302,10 @@ ends iglobal align 4 ohci_hardware_func: + dd USBHC_VERSION dd 'OHCI' dd sizeof.ohci_controller + dd ohci_kickoff_bios dd ohci_init dd ohci_process_deferred dd ohci_set_device_address @@ -299,21 +313,50 @@ ohci_hardware_func: dd ohci_port_disable dd ohci_new_port.reset dd ohci_set_endpoint_packet_size - dd usb1_allocate_endpoint - dd usb1_free_endpoint + dd ohci_alloc_pipe + dd ohci_free_pipe dd ohci_init_pipe dd ohci_unlink_pipe - dd usb1_allocate_general_td - dd usb1_free_general_td + dd ohci_alloc_gtd + dd ohci_free_gtd dd ohci_alloc_transfer dd ohci_insert_transfer dd ohci_new_device +ohci_name db 'OHCI',0 endg ; ============================================================================= ; =================================== 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. ; Called from usb_init_controller. Initializes the hardware and ; OHCI-specific parts of software structures. @@ -338,7 +381,7 @@ if ohci_controller.IntEDs >= 0x1000 .err assertion failed end if lea esi, [eax+ohci_controller.IntEDs+32*sizeof.ohci_static_ep] - call get_pg_addr + invoke GetPgAddr add eax, ohci_controller.IntEDs movi ecx, 32 mov edx, ecx @@ -383,23 +426,14 @@ end if call ohci_init_static_endpoint ; 4. Create a virtual memory area to talk with the controller. ; 4a. Enable memory & bus master access. - mov ch, [.bus] - mov cl, 0 - mov eax, ecx - mov bh, [.devfn] - mov bl, 4 - call pci_read_reg + invoke PciRead16, dword [.bus], dword [.devfn], 4 or al, 6 - xchg eax, ecx - call pci_write_reg + invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax ; 4b. Read memory base address. - mov ah, [.bus] - mov al, 2 - mov bl, 10h - call pci_read_reg + invoke PciRead32, dword [.bus], dword [.devfn], 10h and al, not 0Fh ; 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 jz .fail stosd ; fill ohci_controller.MMIOBase @@ -422,7 +456,7 @@ end if mov [edi+OhciCommandStatusReg], ecx @@: mov esi, ecx - call delay_ms + invoke Sleep test [edi+OhciCommandStatusReg], ecx jz .resetdone dec edx @@ -454,7 +488,7 @@ end if pop esi ; restore pointer to ohci_controller saved in step 1 ; 6a. Physical address of HCCA. mov eax, esi - call get_pg_addr + invoke GetPgAddr mov [edi+OhciHCCAReg], eax ; 6b. Transition to operational state and clear all Enable bits. mov cl, 2 shl 6 @@ -475,28 +509,29 @@ end if ; mov [edi+OhciDoneHeadReg], eax ; 6e. Enable processing of all lists with control:bulk ratio = 1:1. 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 + 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] and eax, 0xF 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] mov [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], eax -; 9. Hook interrupt. - mov ah, [.bus] - mov al, 0 - mov bh, [.devfn] - mov bl, 3Ch - call pci_read_reg +; 10. Hook interrupt. + invoke PciRead8, dword [.bus], dword [.devfn], 3Ch ; al = IRQ movzx eax, al - stdcall attach_int_handler, eax, ohci_irq, esi -; 10. Enable controller interrupt on HcDoneHead writeback and RootHubStatusChange. + invoke AttachIntHandler, eax, ohci_irq, esi +; 11. Enable controller interrupt on HcDoneHead writeback and RootHubStatusChange. 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] -; 11. Initialize ports of the controller. -; 11a. Initiate power up, disable all ports, clear all "changed" bits. +; 12. Initialize ports of the controller. +; 12a. Initiate power up, disable all ports, clear all "changed" bits. mov dword [edi+OhciRhStatusReg], 10000h ; SetGlobalPower xor ecx, ecx @@: @@ -504,20 +539,20 @@ end if inc ecx cmp ecx, [esi+usb_controller.NumPorts] 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 push esi mov esi, [edi+OhciRhDescriptorAReg] shr esi, 24 add esi, esi jz @f - call delay_ms + invoke Sleep @@: 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 ; 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. xor ecx, ecx .port_loop: @@ -527,23 +562,23 @@ end if ; and save the connected time. ; Note that ConnectedTime must be set before 'connected' mark, ; 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 lock bts [esi+usb_controller.NewConnected], ecx .next_port: inc ecx cmp ecx, [esi+usb_controller.NumPorts] jb .port_loop -; 12. Return pointer to usb_controller. +; 13. Return pointer to usb_controller. xchg eax, esi ret .fail_unmap: -; On error after step 4, release the virtual memory area. - stdcall free_kernel_space, edi +; On error after step 5, release the virtual memory area. + invoke FreeKernelSpace, edi .fail: ; On error, free the ohci_controller structure and return zero. ; 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. pop ecx .nothing: @@ -561,7 +596,7 @@ proc ohci_init_static_endpoint mov [edi+ohci_static_ep.NextED], eax mov [edi+ohci_static_ep.NextList], esi 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 ret endp @@ -591,14 +626,10 @@ endp ; deal with USB devices. proc ohci_kickoff_bios ; 1. Get the physical address of MMIO registers. - mov ah, [esi+PCIDEV.bus] - mov bh, [esi+PCIDEV.devfn] - mov al, 2 - mov bl, 10h - call pci_read_reg + invoke PciRead32, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 10h and al, not 0Fh ; 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 jz .nothing ; 3. Some BIOSes enable controller interrupts as a result of giving @@ -625,7 +656,7 @@ proc ohci_kickoff_bios jz .has_ownership push esi movi esi, 1 - call delay_ms + invoke Sleep pop esi loop @b 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. popf ; 7. Release memory mapping created in step 2 and return. - stdcall free_kernel_space, eax + invoke FreeKernelSpace, eax .nothing: ret endp @@ -703,7 +734,8 @@ end virtual xor ebx, ebx test ecx, ecx jz .tddone - call usb_td_to_virt + mov eax, [ohci_gtd_first_page] + invoke usbhc_api.usb_td_to_virt test eax, eax jz .tddone lea edx, [eax+ohci_gtd.NextTD] @@ -718,7 +750,8 @@ end virtual lea ebx, [eax+sizeof.ohci_gtd] test ecx, ecx jz .tddone - call usb_td_to_virt + mov eax, [ohci_gtd_first_page] + invoke usbhc_api.usb_td_to_virt test eax, eax jnz .tdloop .tddone: @@ -845,7 +878,7 @@ end virtual jz .disconnect ; Note: ConnectedTime must be stored before setting the 'connected' bit, ; otherwise ohci_process_deferred could use an old time. - mov eax, [timer_ticks] + invoke GetTimerTicks mov [esi+usb_controller.ConnectedTime+ecx*4], eax lock bts [esi+usb_controller.NewConnected], ecx jmp .nextport @@ -858,8 +891,8 @@ end virtual jz .nextport test al, 10h jnz .nextport - mov edx, [timer_ticks] - mov [esi+usb_controller.ResetTime], edx + invoke GetTimerTicks + mov [esi+usb_controller.ResetTime], eax mov [esi+usb_controller.ResettingStatus], 2 inc ebx .nextport: @@ -871,7 +904,7 @@ end virtual ; 12. Restore the stack after step 6. pop eax ; 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. mov al, 1 pop edi esi ebx ; restore used registers to be stdcall @@ -884,8 +917,7 @@ endp proc ohci_set_device_address mov byte [ebx+ohci_pipe.Flags-sizeof.ohci_pipe], cl ; Wait until the hardware will forget the old value. - call usb_subscribe_control - ret + jmp [usbhc_api.usb_subscribe_control] endp ; This procedure returns USB device address from the usb_pipe structure. @@ -914,8 +946,7 @@ endp proc ohci_set_endpoint_packet_size mov byte [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe], cl ; Wait until the hardware will forget the old value. - call usb_subscribe_control - ret + jmp [usbhc_api.usb_subscribe_control] endp ; This procedure is called from API usb_open_pipe and processes @@ -938,7 +969,7 @@ virtual at ebp-12 end virtual ; 1. Initialize the queue of transfer descriptors: empty. sub eax, sizeof.ohci_gtd - call get_phys_addr + invoke GetPhysAddr mov [edi+ohci_pipe.TailP-sizeof.ohci_pipe], eax mov [edi+ohci_pipe.HeadP-sizeof.ohci_pipe], eax ; 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 [edi+ohci_pipe.NextED-sizeof.ohci_pipe], ecx lea eax, [edi-sizeof.ohci_pipe] - call get_phys_addr + invoke GetPhysAddr mov [edx+ohci_pipe.NextED-sizeof.ohci_pipe], eax ; 4. Return something non-zero. ret @@ -1114,7 +1145,7 @@ endl .fail: mov edi, ohci_hardware_func mov eax, [td] - stdcall usb_undo_tds, [origTD] + invoke usbhc_api.usb_undo_tds, [origTD] xor eax, eax ret endp @@ -1137,18 +1168,18 @@ virtual at ebp-8 .direction dd ? end virtual ; 1. Allocate the next TD. - call usb1_allocate_general_td + call ohci_alloc_gtd test eax, eax jz .nothing ; 2. Initialize controller-independent parts of both TDs. push eax - call usb_init_transfer + invoke usbhc_api.usb_init_transfer pop eax ; 3. Save the returned value (next descriptor). push eax ; 4. Store the physical address of the next descriptor. sub eax, sizeof.ohci_gtd - call get_phys_addr + invoke GetPhysAddr mov [ecx+ohci_gtd.NextTD-sizeof.ohci_gtd], eax ; 5. For zero-length transfers, store zero in both fields for buffer addresses. ; Otherwise, fill them with real values. @@ -1158,12 +1189,12 @@ end virtual cmp [.packetSize], eax jz @f mov eax, [.buffer] - call get_phys_addr + invoke GetPhysAddr mov [ecx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd], eax mov eax, [.buffer] add eax, [.packetSize] dec eax - call get_phys_addr + invoke GetPhysAddr mov [ecx+ohci_gtd.BufEnd-sizeof.ohci_gtd], eax @@: ; 6. Generate Flags field: @@ -1225,7 +1256,9 @@ proc ohci_port_after_reset xor eax, eax xchg al, [esi+usb_controller.ResettingStatus] 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), ; continue to next device (if there is one). movzx ecx, [esi+usb_controller.ResettingPort] @@ -1233,13 +1266,13 @@ proc ohci_port_after_reset test al, 2 jnz @f 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 ; 2. Get LowSpeed bit to bit 0 of eax and call the worker procedure ; to notify the protocol layer about new OHCI device. 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 call ohci_new_device pop ecx @@ -1249,7 +1282,7 @@ proc ohci_port_after_reset jnz .nothing .disable_exit: mov dword [edi+OhciRhPortStatusReg+ecx*4], 1 - jmp usb_test_pending_port + jmp [usbhc_api.usb_test_pending_port] .nothing: ret endp @@ -1272,7 +1305,7 @@ proc ohci_new_device sub esp, 12 ; ignored fields push eax ; .Flags ; 4. Notify the protocol layer. - call usb_new_device + invoke usbhc_api.usb_new_device ; 5. Cleanup the stack after step 3 and return. add esp, 20 ret @@ -1287,7 +1320,7 @@ proc ohci_process_deferred ; 1. Initialize the return value. push -1 ; 2. Process disconnect events. - call usb_disconnect_stage2 + invoke usbhc_api.usb_disconnect_stage2 ; 3. Check for connected devices. ; 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. @@ -1299,7 +1332,15 @@ proc ohci_process_deferred .portloop: bt [esi+usb_controller.NewConnected], ecx 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, USB_CONNECT_DELAY jge .connected @@ -1321,7 +1362,7 @@ proc ohci_process_deferred ; 4. Check for end of reset signalling. If so, call ohci_port_after_reset. cmp [esi+usb_controller.ResettingStatus], 2 jnz .no_reset_recovery - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+usb_controller.ResetTime] sub eax, USB_RESET_RECOVERY_TIME jge .reset_done @@ -1353,7 +1394,7 @@ proc ohci_process_deferred ; 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. ; 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. test eax, eax jz @f @@ -1402,7 +1443,7 @@ proc ohci_process_finalized_td jmp .next_td2 @@: ; 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. ; If CurBufPtr is zero, everything was transferred. xor edx, edx @@ -1429,30 +1470,19 @@ proc ohci_process_finalized_td neg edx ; 4. Check for error. If so, go to 7. push ebx - mov eax, [ebx+ohci_gtd.Flags-sizeof.ohci_gtd] - shr eax, 28 + mov ecx, [ebx+ohci_gtd.Flags-sizeof.ohci_gtd] + shr ecx, 28 jnz .error .notify: ; 5. Successful completion. -; 5a. Check whether this descriptor has an associated callback. - 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 + invoke usbhc_api.usb_process_gtd .next_td: ; 6. Free the current descriptor and advance to the next item. ; If the current item is the last in the list, ; set DoneListEndPtr to pointer to DoneList. cmp ebx, [esp] jz @f - stdcall usb1_free_general_td, ebx + stdcall ohci_free_gtd, ebx @@: pop ebx 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. test ebx, ebx jz @f - stdcall usb1_free_general_td, ebx + stdcall ohci_free_gtd, ebx @@: pop ebx ret @@ -1485,7 +1515,7 @@ proc ohci_process_finalized_td ; 7. There was an error while processing this descriptor. ; The hardware has stopped processing the queue. ; 7a. Save status and length. - push eax + push ecx push edx ; 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] @@ -1496,7 +1526,7 @@ proc ohci_process_finalized_td ; for this transfer. ; Free and unlink non-final descriptors, except the current one. ; Final descriptor will be freed in step 6. - call usb_is_final_packet + invoke usbhc_api.usb_is_final_packet jnc .found_final mov ebx, [ebx+usb_gtd.NextVirt] virtual at esp @@ -1505,11 +1535,11 @@ virtual at esp .current_item dd ? end virtual .look_final: - call usb_unlink_td - call usb_is_final_packet + invoke usbhc_api.usb_unlink_td + invoke usbhc_api.usb_is_final_packet jnc .found_final push [ebx+usb_gtd.NextVirt] - stdcall usb1_free_general_td, ebx + stdcall ohci_free_gtd, ebx pop ebx jmp .look_final .found_final: @@ -1530,14 +1560,14 @@ end virtual .advance_queue: mov eax, [ebx+usb_gtd.NextVirt] sub eax, sizeof.ohci_gtd - call get_phys_addr + invoke GetPhysAddr or eax, edx mov [ecx+ohci_pipe.HeadP-sizeof.ohci_pipe], eax push ebx mov ebx, ecx call ohci_notify_new_work pop ebx - pop edx eax + pop edx ecx jmp .notify ; 7d. Abort the entire transfer. ; There are two cases: either there is only one transfer stage @@ -1553,10 +1583,10 @@ end virtual cmp ebx, [.current_item] push [ebx+usb_gtd.NextVirt] jz @f - stdcall usb1_free_general_td, ebx + stdcall ohci_free_gtd, ebx @@: pop ebx - call usb_unlink_td + invoke usbhc_api.usb_unlink_td .halted: ; 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). @@ -1597,3 +1627,60 @@ proc ohci_unlink_pipe mov [eax+ohci_pipe.NextED-sizeof.ohci_pipe], edx ret 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 diff --git a/kernel/trunk/bus/usb/uhci.inc b/drivers/usb/uhci.asm similarity index 91% rename from kernel/trunk/bus/usb/uhci.inc rename to drivers/usb/uhci.asm index d78c12f19c..c2e9777aec 100644 --- a/kernel/trunk/bus/usb/uhci.inc +++ b/drivers/usb/uhci.asm @@ -1,7 +1,17 @@ ; 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 -; development, but there are no reasons to keep it here. + +; Standard driver stuff +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 ================================= @@ -145,6 +155,8 @@ DeferredActions dd ? LastPollTime dd ? ; See the comment before UHCI_POLL_INTERVAL. This variable keeps the ; last time, in timer ticks, when the polling was done. +EhciCompanion dd ? +; Pointer to usb_controller for EHCI companion, if any, or NULL. ends if uhci_controller.IntEDs mod 16 @@ -242,8 +254,10 @@ ends iglobal align 4 uhci_hardware_func: + dd USBHC_VERSION dd 'UHCI' dd sizeof.uhci_controller + dd uhci_kickoff_bios dd uhci_init dd uhci_process_deferred dd uhci_set_device_address @@ -251,21 +265,50 @@ uhci_hardware_func: dd uhci_port_disable dd uhci_new_port.reset dd uhci_set_endpoint_packet_size - dd usb1_allocate_endpoint + dd uhci_alloc_pipe dd uhci_free_pipe dd uhci_init_pipe dd uhci_unlink_pipe - dd usb1_allocate_general_td + dd uhci_alloc_td dd uhci_free_td dd uhci_alloc_transfer dd uhci_insert_transfer dd uhci_new_device +uhci_name db 'UHCI',0 endg ; ============================================================================= ; =================================== 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. ; Called from usb_init_controller. Initializes the hardware and ; UHCI-specific parts of software structures. @@ -299,7 +342,7 @@ if (uhci_controller.IntEDs mod 0x1000) <> 0 .err assertion failed end if add eax, uhci_controller.IntEDs - call get_phys_addr + invoke GetPhysAddr ; 2b. Fill first 32 entries. inc eax inc eax ; set QH bit for uhci_pipe.NextQH @@ -349,35 +392,20 @@ end if call uhci_init_static_endpoint ; 4. Get I/O base address and size from PCI bus. ; 4a. Read&save PCI command state. - mov bh, [.devfn] - mov ch, [.bus] - mov cl, 1 - mov eax, ecx - mov bl, 4 - call pci_read_reg + invoke PciRead16, dword [.bus], dword [.devfn], 4 push eax ; 4b. Disable IO access. and al, not 1 - push ecx - xchg eax, ecx - call pci_write_reg - pop ecx + invoke PciWrite16, dword [.bus], dword [.devfn], 4, eax ; 4c. Read&save IO base address. - mov eax, ecx - mov bl, 20h - call pci_read_reg + invoke PciRead16, dword [.bus], dword [.devfn], 20h and al, not 3 xchg eax, edi ; now edi = IO base ; 4d. Write 0xffff to IO base address. - push ecx - xchg eax, ecx - or ecx, -1 - call pci_write_reg - pop ecx + invoke PciWrite16, dword [.bus], dword [.devfn], 20h, -1 ; 4e. Read IO base address. - mov eax, ecx - call pci_read_reg + invoke PciRead16, dword [.bus], dword [.devfn], 20h and al, not 3 cwde not eax @@ -385,18 +413,11 @@ end if xchg eax, esi ; now esi = IO size ; 4f. Restore IO base address. - xchg eax, ecx - mov ecx, edi - push eax - call pci_write_reg - pop eax + invoke PciWrite16, dword [.bus], dword [.devfn], 20h, edi ; 4g. Restore PCI command state and enable io & bus master access. pop ecx or ecx, 5 - mov bl, 4 - push eax - call pci_write_reg - pop eax + invoke PciWrite16, dword [.bus], dword [.devfn], 4, ecx ; 5. Reset the controller. ; 5e. Host reset. mov edx, edi @@ -407,7 +428,7 @@ end if @@: push esi movi esi, 1 - call delay_ms + invoke Sleep pop esi in ax, dx test al, 2 @@ -421,7 +442,7 @@ if 0 ; wait 10 ms push esi movi esi, 10 - call delay_ms + invoke Sleep pop esi ; clear reset signal xor eax, eax @@ -458,56 +479,58 @@ end if 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] mov [esi+uhci_controller.IOBase-sizeof.uhci_controller], edi - mov eax, [timer_ticks] + invoke GetTimerTicks mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax -; 8. Hook interrupt. - mov ah, [.bus] - mov al, 0 - mov bh, [.devfn] - mov bl, 3Ch - call pci_read_reg +; 8. Find the EHCI companion. +; If there is one, check whether all ports are covered by that companion. +; Note: this assumes that EHCI is initialized before USB1 companions. + mov ebx, dword [.devfn] + invoke usbhc_api.usb_find_ehci_companion + mov [esi+uhci_controller.EhciCompanion-sizeof.uhci_controller], eax +; 9. Hook interrupt. + invoke PciRead8, dword [.bus], dword [.devfn], 3Ch ; al = IRQ ; DEBUGF 1,'K : UHCI %x: io=%x, irq=%x\n',esi,edi,al movzx eax, al - stdcall attach_int_handler, eax, uhci_irq, esi -; 9. Setup controller registers. + invoke AttachIntHandler, eax, uhci_irq, esi +; 10. Setup controller registers. xor eax, eax 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). inc edx inc edx ; UhciStatusReg == 2 mov al, 3Fh out dx, ax -; 9b. UhciInterruptReg := 0Dh. +; 10b. UhciInterruptReg := 0Dh. inc edx inc edx ; UhciInterruptReg == 4 mov al, 0Dh out dx, ax -; 9c. UhciFrameNumberReg := 0. +; 10c. UhciFrameNumberReg := 0. inc edx inc edx ; UhciFrameNumberReg == 6 mov al, 0 out dx, ax -; 9d. UhciBaseAddressReg := physical address of uhci_controller. +; 10d. UhciBaseAddressReg := physical address of uhci_controller. inc edx inc edx ; UhciBaseAddressReg == 8 lea eax, [esi-sizeof.uhci_controller] - call get_phys_addr + invoke GetPhysAddr out dx, eax -; 9e. UhciCommandReg := Run + Configured + (MaxPacket is 64 bytes) +; 10e. UhciCommandReg := Run + Configured + (MaxPacket is 64 bytes) sub edx, UhciBaseAddressReg ; UhciCommandReg == 0 mov ax, 0C1h ; Run, Configured, MaxPacket = 64b out dx, ax -; 10. Do initial scan of existing devices. +; 11. Do initial scan of existing devices. call uhci_poll_roothub -; 11. Return pointer to usb_controller. +; 12. Return pointer to usb_controller. xchg eax, esi ret .fail: ; 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 -; after step 7. +; Note that the main code branch restores the stack at step 8 and never fails +; after step 8. pop ecx xor eax, eax ret @@ -518,11 +541,7 @@ endp ; so do it in inpolite way, preventing controller from any SMI activity. proc uhci_kickoff_bios ; 1. Get the I/O address. - mov ah, [esi+PCIDEV.bus] - mov al, 1 - mov bh, [esi+PCIDEV.devfn] - mov bl, 20h - call pci_read_reg + invoke PciRead16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 20h and eax, 0xFFFC xchg eax, edx ; 2. Stop the controller and disable all interrupts. @@ -534,11 +553,7 @@ proc uhci_kickoff_bios out dx, ax ; 3. Disable all bits for SMI routing, clear SMI routing status, ; enable master interrupt bit. - mov ah, [esi+PCIDEV.bus] - mov al, 1 - mov bl, 0xC0 - mov ecx, 0AF00h - call pci_write_reg + invoke PciWrite16, dword [esi+PCIDEV.bus], dword [esi+PCIDEV.devfn], 0xC0, 0AF00h ret endp @@ -552,7 +567,7 @@ proc uhci_init_static_endpoint mov byte [edi+uhci_static_ep.HeadTD], 1 mov [edi+uhci_static_ep.NextList], esi 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 ret endp @@ -612,7 +627,7 @@ end virtual push ebx xor ebx, ebx inc ebx - call usb_wakeup_if_needed + invoke usbhc_api.usb_wakeup_if_needed pop ebx ; 6. This is our interrupt; return 1. mov al, 1 @@ -633,12 +648,12 @@ proc uhci_process_deferred ; the error can be caused by disconnect, try to detect it. test byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], 2 jnz .force_poll - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+uhci_controller.LastPollTime-sizeof.uhci_controller] sub eax, UHCI_POLL_INTERVAL jl .nopoll .force_poll: - mov eax, [timer_ticks] + invoke GetTimerTicks mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax call uhci_poll_roothub mov eax, -UHCI_POLL_INTERVAL @@ -675,7 +690,7 @@ proc uhci_process_deferred @@: ; 4. Process disconnect events. This should be done after step 2 ; (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. ; Call uhci_new_port for all such devices. xor ecx, ecx @@ -684,7 +699,15 @@ proc uhci_process_deferred .portloop: bt [esi+usb_controller.NewConnected], ecx 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, USB_CONNECT_DELAY jge .connected @@ -721,7 +744,7 @@ proc uhci_process_deferred cmp [esi+usb_controller.ResettingStatus], 1 jnz .no_reset_in_progress ; 7b. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+usb_controller.ResetTime] sub eax, USB_RESET_TIME jge .reset_done @@ -739,7 +762,7 @@ proc uhci_process_deferred cmp [esi+usb_controller.ResettingStatus], 0 jz .skip_reset ; 7f. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] + invoke GetTimerTicks sub eax, [esi+usb_controller.ResetTime] sub eax, USB_RESET_RECOVERY_TIME jge .reset_recovery_done @@ -758,7 +781,7 @@ proc uhci_process_deferred ; 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. ; 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. test eax, eax 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) ; or b) an active (not yet processed by the hardware) descriptor is reached. lea ecx, [ebx+usb_pipe.Lock] - call mutex_lock + invoke MutexLock mov ebx, [ebx+usb_pipe.LastTD] push ebx mov ebx, [ebx+usb_gtd.NextVirt] @@ -847,13 +870,13 @@ proc uhci_process_updated_list ; Release the queue lock while processing one descriptor: ; callback function could (and often would) schedule another transfer. push ecx - call mutex_unlock + invoke MutexUnlock call uhci_process_finalized_td pop ecx - call mutex_lock + invoke MutexLock jmp .tdloop .tddone: - call mutex_unlock + invoke MutexUnlock pop ebx ; End of internal loop, restore pointer to the next pipe ; and continue the external loop. @@ -871,7 +894,7 @@ endp ; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd. proc uhci_process_finalized_td ; 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 : %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] @@ -958,12 +981,12 @@ end if ; for this transfer. ; Free and unlink non-final descriptors, except the current one. ; Final descriptor will be freed in step 7. - call usb_is_final_packet + invoke usbhc_api.usb_is_final_packet jnc .found_final mov ebx, [ebx+usb_gtd.NextVirt] .look_final: - call usb_unlink_td - call usb_is_final_packet + invoke usbhc_api.usb_unlink_td + invoke usbhc_api.usb_is_final_packet jnc .found_final push [ebx+usb_gtd.NextVirt] stdcall uhci_free_td, ebx @@ -1040,7 +1063,7 @@ end if stdcall uhci_free_td, ebx @@: pop ebx - call usb_unlink_td + invoke usbhc_api.usb_unlink_td pop ecx .normal: ; 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, ; or all necessary error actions were taken and ebx points to the last ; related descriptor. -; 6a. Test whether it is the last packet in the transfer -; <=> 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: + invoke usbhc_api.usb_process_gtd ; 7. Free the current descriptor (if allowed) and return the next one. ; 7a. Save pointer to the next descriptor. push [ebx+usb_gtd.NextVirt] @@ -1113,7 +1122,7 @@ proc uhci_fix_toggle jz .nothing ; 3. Lock the transfer queue. add ecx, usb_pipe.Lock - call mutex_lock + invoke MutexLock ; 4. Flip the toggle bit in all packets from ebx.NextVirt to ecx.LastTD ; (inclusive). 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) or dword [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock], eax ; 6. Unlock the transfer queue. - call mutex_unlock + invoke MutexUnlock .nothing: ret endp @@ -1164,14 +1173,14 @@ proc uhci_poll_roothub ; Some controllers set enable status change bit, some don't. test bl, 2 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; ; if there is something connected, store the connect time and note connect event. ; In any way, do not process bts [esi+usb_controller.NewDisconnected], ecx test al, 1 jz .disconnect - mov eax, [timer_ticks] + invoke GetTimerTicks mov [esi+usb_controller.ConnectedTime+ecx*4], eax bts [esi+usb_controller.NewConnected], ecx jmp .nextport @@ -1222,7 +1231,7 @@ proc uhci_new_port or ah, 2 out dx, ax ; 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.ResettingStatus], 1 .nothing: @@ -1237,14 +1246,14 @@ proc uhci_port_reset_done mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] lea edx, [edx+ecx*2+UhciPort1StatusReg] 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 out dx, ax ; 2. Status bits in UHCI are invalid during reset signalling. ; Wait a millisecond while status bits become valid again. push esi movi esi, 1 - call delay_ms + invoke Sleep pop esi ; 3. ConnectStatus bit is zero during reset and becomes 1 during step 2; ; 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 out dx, ax ; 4. Store the current time and set status to 2 = reset recovery active. - mov eax, [timer_ticks] - DEBUGF 1,'K : reset done at %d\n',[timer_ticks] + invoke GetTimerTicks + DEBUGF 1,'K : reset done\n' mov [esi+usb_controller.ResetTime], eax mov [esi+usb_controller.ResettingStatus], 2 ret @@ -1272,12 +1281,12 @@ proc uhci_port_init mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] lea edx, [edx+ecx*2+UhciPort1StatusReg] 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. test al, 1 jnz @f 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 ; to notify the protocol layer about new UHCI device. @@ -1293,7 +1302,7 @@ proc uhci_port_init in ax, dx and al, not 4 out dx, ax ; disable the port - jmp usb_test_pending_port + jmp [usbhc_api.usb_test_pending_port] .nothing: ret endp @@ -1316,7 +1325,7 @@ proc uhci_new_device push eax ; ignored (ErrorTD) push eax ; .Token field: DeviceAddress is zero, bit 20 = LowSpeedDevice ; 4. Notify the protocol layer. - call usb_new_device + invoke usbhc_api.usb_new_device ; 5. Cleanup the stack after step 3 and return. add esp, 12 ret @@ -1327,8 +1336,7 @@ endp ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address proc uhci_set_device_address mov byte [ebx+uhci_pipe.Token+1-sizeof.uhci_pipe], cl - call usb_subscription_done - ret + jmp [usbhc_api.usb_subscription_done] endp ; 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 ; uhci_pipe.Token field is purely for software bookkeeping and does not affect ; the hardware; thus, we can continue initialization immediately. - call usb_subscription_done - ret + jmp [usbhc_api.usb_subscription_done] endp ; 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. push eax ; store pointer to the first TD for step 4 sub eax, sizeof.uhci_gtd - call get_phys_addr + invoke GetPhysAddr mov [edi+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax ; 3. Initialize Token field: ; 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 [edi+uhci_pipe.NextQH-sizeof.uhci_pipe], ecx lea eax, [edi-sizeof.uhci_pipe] - call get_phys_addr + invoke GetPhysAddr inc eax inc eax mov [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart], eax @@ -1512,18 +1519,6 @@ proc uhci_unlink_pipe 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 -@@: - jmp usb1_free_endpoint -endp - ; This procedure is called from the several places in main USB code ; and allocates required packets for the given transfer stage. ; ebx = pipe, other parameters are passed through the stack @@ -1612,7 +1607,7 @@ endl .fail: mov edi, uhci_hardware_func mov eax, [td] - stdcall usb_undo_tds, [origTD] + invoke usbhc_api.usb_undo_tds, [origTD] xor eax, eax jmp .nothing endp @@ -1652,7 +1647,7 @@ end virtual mov eax, [.packetSize] add eax, eax add eax, sizeof.uhci_original_buffer - call malloc + invoke Kmalloc ; 1d. If failed, return zero. test eax, eax jz .nothing @@ -1695,14 +1690,14 @@ end virtual .notempbuf: ; 2. Allocate the next TD. push eax - call usb1_allocate_general_td + call uhci_alloc_td pop edx ; If failed, free the temporary buffer (if it was allocated) and return zero. test eax, eax jz .fail ; 3. Initialize controller-independent parts of both TDs. push edx - call usb_init_transfer + invoke usbhc_api.usb_init_transfer ; 4. Initialize the next TD: ; 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, @@ -1725,7 +1720,7 @@ end virtual ; 5b. Store physical address of the next TD. push eax sub eax, sizeof.uhci_gtd - call get_phys_addr + invoke GetPhysAddr ; for Control/Bulk pipes, use Depth traversal unless this is the first TD ; in the transfer stage; ; uhci_insert_transfer will set Depth traversal for the first TD and clear @@ -1748,7 +1743,7 @@ end virtual jz @f mov eax, [edx+uhci_original_buffer.UsedBuffer] @@: - call get_phys_addr + invoke GetPhysAddr .hasphysbuf: mov [ecx+uhci_gtd.Buffer-sizeof.uhci_gtd], eax ; 5d. For IN transfers, disallow short packets. @@ -1774,7 +1769,7 @@ end virtual ret .fail: xchg eax, edx - call free + invoke Kfree xor eax, eax ret endp @@ -1796,6 +1791,47 @@ proc uhci_insert_transfer ret 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. ; For UHCI, this includes memory for uhci_gtd itself ; and the temporary buffer, if present. @@ -1804,10 +1840,23 @@ proc uhci_free_td mov eax, [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] and eax, not 1 jz .nobuf - push ebx - call free - pop ebx + invoke Kfree .nobuf: sub dword [esp+4], sizeof.uhci_gtd - jmp usb_free_common + jmp [usbhc_api.usb_free_common] 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 diff --git a/drivers/usb/usb1_scheduler.inc b/drivers/usb/usb1_scheduler.inc new file mode 100644 index 0000000000..939736feef --- /dev/null +++ b/drivers/usb/usb1_scheduler.inc @@ -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 diff --git a/kernel/trunk/bus/pci/pci32.inc b/kernel/trunk/bus/pci/pci32.inc index 6a5c67ecfe..e82e8e83ec 100644 --- a/kernel/trunk/bus/pci/pci32.inc +++ b/kernel/trunk/bus/pci/pci32.inc @@ -694,7 +694,8 @@ end virtual mov [ecx+PCIDEV.fd], edi mov [eax+PCIDEV.bk], edi 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 al, 2 mov bl, 8 diff --git a/kernel/trunk/bus/usb/hccommon.inc b/kernel/trunk/bus/usb/hccommon.inc index 446b5670d8..724000d088 100644 --- a/kernel/trunk/bus/usb/hccommon.inc +++ b/kernel/trunk/bus/usb/hccommon.inc @@ -1,238 +1,33 @@ ; USB Host Controller support code: hardware-independent part, ; common for all controller types. -; ============================================================================= -; ================================= Constants ================================= -; ============================================================================= -; USB device must have at least 100ms of stable power before initializing can -; proceed; one timer tick is 10ms, so enforce delay in 10 ticks -USB_CONNECT_DELAY = 10 -; USB requires at least 10 ms for reset signalling. Normally, this is one timer -; tick. However, it is possible that we start reset signalling in the end of -; interval between timer ticks and then we test time in the start of the next -; interval; in this case, the delta between [timer_ticks] is 1, but the real -; time passed is significantly less than 10 ms. To avoid this, we add an extra -; tick; this guarantees that at least 10 ms have passed. -USB_RESET_TIME = 2 -; USB requires at least 10 ms of reset recovery, a delay between reset -; signalling and any commands to device. Add an extra tick for the same reasons -; as with the previous constant. -USB_RESET_RECOVERY_TIME = 2 - -; ============================================================================= -; ================================ Structures ================================= -; ============================================================================= -; Controller descriptor. -; This structure represents the common (controller-independent) part -; of a controller for the USB code. The corresponding controller-dependent -; 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 ==================================== -; ============================================================================= +iglobal +; USB HC support: some functions interesting only for *HCI-drivers. +align 4 +usb_hc_func: + dd usb_process_gtd + dd usb_init_static_endpoint + dd usb_wakeup_if_needed + dd usb_subscribe_control + dd usb_subscription_done + dd usb_allocate_common + dd usb_free_common + dd usb_td_to_virt + dd usb_init_transfer + dd usb_undo_tds + dd usb_test_pending_port + dd usb_get_tt + dd usb_get_tt_think_time + dd usb_new_device + dd usb_disconnect_stage2 + dd usb_process_wait_lists + dd usb_unlink_td + dd usb_is_final_packet + dd usb_find_ehci_companion +endg ; 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 push ebp mov ebp, esp @@ -240,6 +35,10 @@ proc usb_init_controller ; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs. push dword [eax+PCIDEV.devfn] push eax + mov edi, [eax+PCIDEV.owner] + test edi, edi + jz .nothing + mov edi, [edi+USBSRV.usb_func] ; 2. Allocate *hci_controller + usb_controller. mov ebx, [edi+usb_hardware_func.DataSize] 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 dec eax ; don't allocate zero address 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] call mutex_init add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock @@ -474,3 +275,48 @@ proc usb_process_one_wait_list .nothing: ret 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 diff --git a/kernel/trunk/bus/usb/hub.inc b/kernel/trunk/bus/usb/hub.inc index 6661b39e6e..f3bdf40315 100644 --- a/kernel/trunk/bus/usb/hub.inc +++ b/kernel/trunk/bus/usb/hub.inc @@ -1262,3 +1262,14 @@ end virtual .nothing: retn 4 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 diff --git a/kernel/trunk/bus/usb/init.inc b/kernel/trunk/bus/usb/init.inc index d43f94910e..8eb16348af 100644 --- a/kernel/trunk/bus/usb/init.inc +++ b/kernel/trunk/bus/usb/init.inc @@ -29,19 +29,20 @@ ; ProcessDeferred and sleeps until this moment is reached or the thread ; 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. proc usb_init ; 1. Initialize all locks. mov ecx, usb_controllers_list_mutex 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 ; *hci_kickoff_bios. Also count USB controllers for the next step. ; Note: USB1 companion(s) must go before the corresponding EHCI controller, @@ -59,18 +60,33 @@ proc usb_init jz .done_kickoff cmp word [esi+PCIDEV.class+1], 0x0C03 jnz .kickoff - mov eax, uhci_kickoff_bios + mov ebx, uhci_service_name cmp byte [esi+PCIDEV.class], 0x00 jz .do_kickoff - mov eax, ohci_kickoff_bios + mov ebx, ohci_service_name cmp byte [esi+PCIDEV.class], 0x10 jz .do_kickoff - mov eax, ehci_kickoff_bios + mov ebx, ehci_service_name cmp byte [esi+PCIDEV.class], 0x20 jnz .kickoff .do_kickoff: 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 .done_kickoff: pop eax @@ -97,7 +113,6 @@ proc usb_init jz .done_ehci cmp [eax+PCIDEV.class], 0x0C0320 jnz .scan_ehci - mov edi, ehci_hardware_func call usb_init_controller jmp .scan_ehci .done_ehci: @@ -108,10 +123,8 @@ proc usb_init mov eax, [eax+PCIDEV.fd] cmp eax, pcidev_list jz .done_usb1 - mov edi, uhci_hardware_func cmp [eax+PCIDEV.class], 0x0C0300 jz @f - mov edi, ohci_hardware_func cmp [eax+PCIDEV.class], 0x0C0310 jnz .scan_usb1 @@: @@ -238,11 +251,8 @@ usb_controllers_list_mutex MUTEX endg include "memory.inc" +include "common.inc" include "hccommon.inc" include "pipe.inc" -include "ohci.inc" -include "uhci.inc" -include "ehci.inc" include "protocol.inc" include "hub.inc" -include "scheduler.inc" diff --git a/kernel/trunk/bus/usb/memory.inc b/kernel/trunk/bus/usb/memory.inc index f8caf54633..480e12b2b8 100644 --- a/kernel/trunk/bus/usb/memory.inc +++ b/kernel/trunk/bus/usb/memory.inc @@ -12,77 +12,6 @@ ; 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. ; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure. proc usb_allocate_common @@ -187,12 +116,12 @@ end virtual ret 4 endp -; Helper procedure for OHCI: translate physical address in ecx +; Helper procedure: translate physical address in ecx ; of some transfer descriptor to linear address. +; in: eax = address of first page proc usb_td_to_virt ; Traverse all pages used for transfer descriptors, looking for the one ; with physical address as in ecx. - mov eax, [usb_gtd_first_page] @@: test eax, eax jz .zero diff --git a/kernel/trunk/bus/usb/pipe.inc b/kernel/trunk/bus/usb/pipe.inc index b88e0d9d51..00be955062 100644 --- a/kernel/trunk/bus/usb/pipe.inc +++ b/kernel/trunk/bus/usb/pipe.inc @@ -1,195 +1,5 @@ ; 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 macro stdcall_verify [arg] { @@ -216,7 +26,7 @@ endp proc usb_open_pipe stdcall uses ebx esi edi,\ config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword 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 bandwidth dd ? target dd ? @@ -810,6 +620,27 @@ proc usb_unlink_td ret 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 proc verify_regs virtual at esp diff --git a/kernel/trunk/bus/usb/protocol.inc b/kernel/trunk/bus/usb/protocol.inc index dc3a68a3e0..8f62b37f92 100644 --- a/kernel/trunk/bus/usb/protocol.inc +++ b/kernel/trunk/bus/usb/protocol.inc @@ -29,11 +29,6 @@ USB_DEVICE_QUALIFIER_DESCR = 6 USB_OTHER_SPEED_CONFIG_DESCR = 7 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 ; read to the debug board. 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. ; Thus, the call is asynchronous; meet us in usb_after_set_address when it will ; be safe to continue. - dbgstr 'address set in device' +; dbgstr 'address set in device' call [eax+usb_hardware_func.SetDeviceAddress] ; 2b. If the port is in non-root hub, clear 'reset in progress' flag. ; In any case, proceed to 4. @@ -409,7 +404,7 @@ endp ; is cleared after request from usb_set_address_callback. ; in: ebx -> usb_pipe 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. ; Remember, we still do not know the actual packet size; ; 8-bytes-request is safe. diff --git a/kernel/trunk/const.inc b/kernel/trunk/const.inc index 62bbf9abbc..27d5c33dcb 100644 --- a/kernel/trunk/const.inc +++ b/kernel/trunk/const.inc @@ -509,6 +509,8 @@ struct PCIDEV class dd ? devfn db ? bus db ? + rb 2 + owner dd ? ; pointer to SRV or 0 ends ; The following macro assume that we are on uniprocessor machine. diff --git a/kernel/trunk/core/dll.inc b/kernel/trunk/core/dll.inc index 760f5c4a85..fc71e779b3 100644 --- a/kernel/trunk/core/dll.inc +++ b/kernel/trunk/core/dll.inc @@ -214,10 +214,35 @@ proc get_service stdcall, sz_name:dword mov edx, [edx+SRV.fd] jmp @B .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 jmp load_driver .ok: mov eax, edx +.nothing: ret endp diff --git a/kernel/trunk/core/exports.inc b/kernel/trunk/core/exports.inc index 02e18a5250..1321edcac7 100644 --- a/kernel/trunk/core/exports.inc +++ b/kernel/trunk/core/exports.inc @@ -45,6 +45,7 @@ __exports: map_io_mem, 'MapIoMem', \ ; stdcall map_page, 'MapPage', \ ; stdcall get_pg_addr, 'GetPgAddr', \ ; eax + get_phys_addr, 'GetPhysAddr', \ ; eax map_space, 'MapSpace', \ release_pages, 'ReleasePages', \ \ @@ -113,6 +114,7 @@ __exports: usb_normal_transfer_async, 'USBNormalTransferAsync', \ usb_control_async, 'USBControlTransferAsync', \ usb_get_param, 'USBGetParam', \ + usb_hc_func, 'USBHCFunc', \ \ NET_add_device, 'NetRegDev', \ NET_remove_device, 'NetUnRegDev', \ diff --git a/kernel/trunk/core/memory.inc b/kernel/trunk/core/memory.inc index 9749a4f5b7..36a15a10fd 100644 --- a/kernel/trunk/core/memory.inc +++ b/kernel/trunk/core/memory.inc @@ -1253,22 +1253,7 @@ f68: cmp edx, OS_BASE jae .fail - mov edi, 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 - -@@: + stdcall load_pe_driver, ecx, edx mov [esp+32], eax ret .22: @@ -1348,26 +1333,32 @@ f68call: ; keep this table closer to main code align 4 -proc load_pe_driver stdcall, file:dword +proc load_pe_driver stdcall, file:dword, cmdline:dword + push esi stdcall load_PE, [file] test eax, eax jz .fail mov esi, eax - stdcall eax, DRV_ENTRY + push [cmdline] + push DRV_ENTRY + call eax + pop ecx + pop ecx test eax, eax jz .fail mov [eax+SRV.entry], esi + pop esi ret .fail: xor eax, eax + pop esi ret endp - align 4 proc init_mtrr @@ -1415,9 +1406,9 @@ proc init_mtrr xor eax, eax xor edx, edx @@: - wrmsr inc ecx - cmp ecx, 0x210 + wrmsr + cmp ecx, 0x20F jb @b ; enable MTRRs pop eax diff --git a/kernel/trunk/core/peload.inc b/kernel/trunk/core/peload.inc index 05ef51d639..e0cfd05f8b 100644 --- a/kernel/trunk/core/peload.inc +++ b/kernel/trunk/core/peload.inc @@ -85,13 +85,12 @@ L3: mov ecx, eax add edi, DWORD PTR [edx+260] + add ecx, 3 shr ecx, 2 rep movsd L4: mov ecx, DWORD PTR [edx+256] - add ecx, 4095 - and ecx, -4096 cmp ecx, eax jbe L6 sub ecx, eax @@ -178,6 +177,10 @@ L23: mov ecx, DWORD PTR [eax-4] mov DWORD PTR [esp+48], edi mov edx, DWORD PTR [eax-20] + test edx, edx + jnz @f + mov edx, ecx +@@: mov DWORD PTR [esp+52], 0 add ecx, ebp add edx, ebp