From 3ed9acd41accaee1d9e6d7b2324bb628a68ff6ff Mon Sep 17 00:00:00 2001 From: "Sergey Semyonov (Serge)" Date: Wed, 1 Jan 2014 21:16:27 +0000 Subject: [PATCH] kolibri-acpi:update git-svn-id: svn://kolibrios.org@4423 a494cfbc-eb01-0410-851d-a64ba20cac60 --- .../branches/Kolibri-acpi/blkdev/bd_drv.inc | 293 +++ .../branches/Kolibri-acpi/blkdev/hd_drv.inc | 395 +--- .../branches/Kolibri-acpi/boot/bootcode.inc | 6 +- .../branches/Kolibri-acpi/bus/usb/common.inc | 446 ++++ kernel/branches/Kolibri-acpi/bus/usb/ehci.inc | 1895 ----------------- .../Kolibri-acpi/bus/usb/hccommon.inc | 306 +-- kernel/branches/Kolibri-acpi/bus/usb/hub.inc | 64 +- kernel/branches/Kolibri-acpi/bus/usb/init.inc | 48 +- .../branches/Kolibri-acpi/bus/usb/memory.inc | 75 +- kernel/branches/Kolibri-acpi/bus/usb/ohci.inc | 1599 -------------- kernel/branches/Kolibri-acpi/bus/usb/pipe.inc | 213 +- .../Kolibri-acpi/bus/usb/protocol.inc | 9 +- .../Kolibri-acpi/bus/usb/scheduler.inc | 1087 ---------- kernel/branches/Kolibri-acpi/bus/usb/uhci.inc | 1813 ---------------- kernel/branches/Kolibri-acpi/const.inc | 2 + kernel/branches/Kolibri-acpi/core/dll.inc | 27 +- kernel/branches/Kolibri-acpi/core/exports.inc | 2 + kernel/branches/Kolibri-acpi/core/heap.inc | 26 +- kernel/branches/Kolibri-acpi/core/memory.inc | 33 +- kernel/branches/Kolibri-acpi/core/peload.inc | 21 +- kernel/branches/Kolibri-acpi/core/sys32.inc | 11 + kernel/branches/Kolibri-acpi/core/taskman.inc | 3 +- .../branches/Kolibri-acpi/detect/vortex86.inc | 141 +- .../branches/Kolibri-acpi/drivers/usbstor.asm | 49 +- kernel/branches/Kolibri-acpi/gui/window.inc | 1 - kernel/branches/Kolibri-acpi/init.inc | 2 + kernel/branches/Kolibri-acpi/kernel.asm | 4 +- kernel/branches/Kolibri-acpi/kernel32.inc | 2 + kernel/branches/Kolibri-acpi/network/IPv4.inc | 19 + kernel/branches/Kolibri-acpi/network/icmp.inc | 2 +- .../Kolibri-acpi/network/loopback.inc | 2 +- .../branches/Kolibri-acpi/network/socket.inc | 39 +- .../branches/Kolibri-acpi/network/stack.inc | 5 +- kernel/branches/Kolibri-acpi/network/tcp.inc | 56 + .../Kolibri-acpi/network/tcp_input.inc | 230 +- .../Kolibri-acpi/network/tcp_output.inc | 105 +- .../Kolibri-acpi/network/tcp_subr.inc | 35 +- .../Kolibri-acpi/network/tcp_usreq.inc | 35 +- .../branches/Kolibri-acpi/video/cursors.inc | 2 + kernel/branches/Kolibri-acpi/video/vesa20.inc | 1 - 40 files changed, 1549 insertions(+), 7555 deletions(-) create mode 100644 kernel/branches/Kolibri-acpi/blkdev/bd_drv.inc create mode 100644 kernel/branches/Kolibri-acpi/bus/usb/common.inc delete mode 100644 kernel/branches/Kolibri-acpi/bus/usb/ehci.inc delete mode 100644 kernel/branches/Kolibri-acpi/bus/usb/ohci.inc delete mode 100644 kernel/branches/Kolibri-acpi/bus/usb/scheduler.inc delete mode 100644 kernel/branches/Kolibri-acpi/bus/usb/uhci.inc diff --git a/kernel/branches/Kolibri-acpi/blkdev/bd_drv.inc b/kernel/branches/Kolibri-acpi/blkdev/bd_drv.inc new file mode 100644 index 0000000000..4ae53cf381 --- /dev/null +++ b/kernel/branches/Kolibri-acpi/blkdev/bd_drv.inc @@ -0,0 +1,293 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4420 $ + + +; Access through BIOS by diamond +iglobal +align 4 +bd_callbacks: + dd bd_callbacks.end - bd_callbacks ; strucsize + dd 0 ; no close function + dd 0 ; no closemedia function + dd bd_querymedia + dd bd_read_interface + dd bd_write_interface + dd 0 ; no flush function + dd 0 ; use default cache size +.end: +endg + +proc bd_read_interface stdcall uses edi, \ + userdata, buffer, startsector:qword, numsectors + ; userdata = old [hdpos] = 80h + index in NumBiosDisks + ; buffer = pointer to buffer for data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really read +locals +sectors_todo dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and edi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov eax, [userdata] + mov [hdpos], eax + mov eax, dword [startsector] + mov edi, [buffer] +; 4. Worker procedures take one sectors per time, so loop over all sectors to read. +.sectors_loop: + call bd_read + cmp [hd_error], 0 + jnz .fail + mov ecx, [numsectors] + inc dword [ecx] ; one more sector is read + dec [sectors_todo] + jz .done + inc eax + jnz .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp + +proc bd_write_interface stdcall uses esi edi, \ + userdata, buffer, startsector:qword, numsectors + ; userdata = old [hdpos] = 80h + index in NumBiosDisks + ; buffer = pointer to buffer with data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really written +locals +sectors_todo dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and esi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov eax, [userdata] + mov [hdpos], eax + mov esi, [buffer] + lea edi, [startsector] + mov [cache_chain_ptr], edi +; 4. Worker procedures take max 16 sectors per time, +; loop until all sectors will be processed. +.sectors_loop: + mov ecx, 16 + cmp ecx, [sectors_todo] + jbe @f + mov ecx, [sectors_todo] +@@: + mov [cache_chain_size], cl + call bd_write_cache_chain + cmp [hd_error], 0 + jnz .fail + movzx ecx, [cache_chain_size] + mov eax, [numsectors] + add [eax], ecx + sub [sectors_todo], ecx + jz .done + add [edi], ecx + jc .fail + shl ecx, 9 + add esi, ecx + jmp .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp + +; This is a stub. +proc bd_querymedia stdcall, hd_data, mediainfo + mov eax, [mediainfo] + mov [eax+DISKMEDIAINFO.Flags], 0 + mov [eax+DISKMEDIAINFO.SectorSize], 512 + or dword [eax+DISKMEDIAINFO.Capacity], 0xFFFFFFFF + or dword [eax+DISKMEDIAINFO.Capacity+4], 0xFFFFFFFF + xor eax, eax + ret +endp + +;----------------------------------------------------------------------------- +; \begin{diamond} +uglobal +bios_hdpos dd 0 ; 0 is invalid value for [hdpos] +bios_cur_sector dd ? +bios_read_len dd ? +endg +;----------------------------------------------------------------------------- +align 4 +bd_read: + push eax + push edx + mov edx, [bios_hdpos] + cmp edx, [hdpos] + jne .notread + mov edx, [bios_cur_sector] + cmp eax, edx + jb .notread + add edx, [bios_read_len] + dec edx + cmp eax, edx + ja .notread + sub eax, [bios_cur_sector] + shl eax, 9 + add eax, (OS_BASE+0x9A000) + push ecx esi + mov esi, eax + mov ecx, 512/4 + cld + rep movsd + pop esi ecx + pop edx + pop eax + ret +.notread: + push ecx + mov dl, 42h + mov ecx, 16 + call int13_call + pop ecx + test eax, eax + jnz .v86err + test edx, edx + jz .readerr + mov [bios_read_len], edx + mov edx, [hdpos] + mov [bios_hdpos], edx + pop edx + pop eax + mov [bios_cur_sector], eax + jmp bd_read +.readerr: +.v86err: + mov [hd_error], 1 + jmp hd_read_error +;----------------------------------------------------------------------------- +align 4 +bd_write_cache_chain: + pusha + mov edi, OS_BASE + 0x9A000 + movzx ecx, [cache_chain_size] + push ecx + shl ecx, 9-2 + rep movsd + pop ecx + mov dl, 43h + mov eax, [cache_chain_ptr] + mov eax, [eax] + call int13_call + test eax, eax + jnz .v86err + cmp edx, ecx + jnz .writeerr + popa + ret +.v86err: +.writeerr: + popa + mov [hd_error], 1 + jmp hd_write_error +;----------------------------------------------------------------------------- +uglobal +int13_regs_in rb sizeof.v86_regs +int13_regs_out rb sizeof.v86_regs +endg +;----------------------------------------------------------------------------- +align 4 +int13_call: +; Because this code uses fixed addresses, +; it can not be run simultaniously by many threads. +; In current implementation it is protected by common mutex 'ide_status' + mov word [OS_BASE + 510h], 10h ; packet length + mov word [OS_BASE + 512h], cx ; number of sectors + mov dword [OS_BASE + 514h], 9A000000h ; buffer 9A00:0000 + mov dword [OS_BASE + 518h], eax + and dword [OS_BASE + 51Ch], 0 + push ebx ecx esi edi + mov ebx, int13_regs_in + mov edi, ebx + mov ecx, sizeof.v86_regs/4 + xor eax, eax + rep stosd + mov byte [ebx+v86_regs.eax+1], dl + mov eax, [hdpos] + lea eax, [BiosDisksData+(eax-80h)*4] + mov dl, [eax] + mov byte [ebx+v86_regs.edx], dl + movzx edx, byte [eax+1] +; mov dl, 5 + test edx, edx + jnz .hasirq + dec edx + jmp @f +.hasirq: + pushad + stdcall enable_irq, edx + popad +@@: + mov word [ebx+v86_regs.esi], 510h + mov word [ebx+v86_regs.ss], 9000h + mov word [ebx+v86_regs.esp], 0A000h + mov word [ebx+v86_regs.eip], 500h + mov [ebx+v86_regs.eflags], 20200h + mov esi, [sys_v86_machine] + mov ecx, 0x502 + push fs + call v86_start + pop fs + and [bios_hdpos], 0 + pop edi esi ecx ebx + movzx edx, byte [OS_BASE + 512h] + test byte [int13_regs_out+v86_regs.eflags], 1 + jnz @f + mov edx, ecx +@@: + ret +; \end{diamond} diff --git a/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc b/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc index 227969880c..ea787c5afd 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc @@ -10,7 +10,6 @@ $Revision$ ; Low-level driver for HDD access ; DMA support by Mario79 -; Access through BIOS by diamond ; LBA48 support by Mario79 ;----------------------------------------------------------------------------- struct HD_DATA @@ -32,17 +31,6 @@ ide_callbacks: dd 0 ; use default cache size .end: -bd_callbacks: - dd bd_callbacks.end - bd_callbacks ; strucsize - dd 0 ; no close function - dd 0 ; no closemedia function - dd bd_querymedia - dd bd_read_interface - dd bd_write_interface - dd 0 ; no flush function - dd 0 ; use default cache size -.end: - hd0_data HD_DATA ?, 0, 1 hd1_data HD_DATA ?, 0x10, 2 hd2_data HD_DATA ?, 0, 3 @@ -112,6 +100,22 @@ endl ja .nodma cmp [dma_hdd], 1 jnz .nodma +;-------------------------------------- + push eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + pop eax + jnz @f + + test [DRIVE_DATA+1], byte 10100000b + jnz .nodma + + jmp .dma +@@: + test [DRIVE_DATA+1], byte 1010b + jnz .nodma +.dma: +;-------------------------------------- call hd_read_dma jmp @f .nodma: @@ -204,6 +208,22 @@ endl jae .nodma cmp [dma_hdd], 1 jnz .nodma +;-------------------------------------- + push eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + pop eax + jnz @f + + test [DRIVE_DATA+1], byte 10100000b + jnz .nodma + + jmp .dma +@@: + test [DRIVE_DATA+1], byte 1010b + jnz .nodma +.dma: +;-------------------------------------- call cache_write_dma jmp .common .nodma: @@ -251,137 +271,6 @@ proc ide_querymedia stdcall, hd_data, mediainfo ret endp -proc bd_read_interface stdcall uses edi, \ - userdata, buffer, startsector:qword, numsectors - ; userdata = old [hdpos] = 80h + index in NumBiosDisks - ; buffer = pointer to buffer for data - ; startsector = 64-bit start sector - ; numsectors = pointer to number of sectors on input, - ; must be filled with number of sectors really read -locals -sectors_todo dd ? -endl -; 1. Initialize number of sectors: get number of requested sectors -; and say that no sectors were read yet. - mov ecx, [numsectors] - mov eax, [ecx] - mov dword [ecx], 0 - mov [sectors_todo], eax -; 2. Acquire the global lock. - mov ecx, ide_mutex - call mutex_lock -; 3. Convert parameters to the form suitable for worker procedures. -; Underlying procedures do not know about 64-bit sectors. -; Worker procedures use global variables and edi for [buffer]. - cmp dword [startsector+4], 0 - jnz .fail - and [hd_error], 0 - mov eax, [userdata] - mov [hdpos], eax - mov eax, dword [startsector] - mov edi, [buffer] -; 4. Worker procedures take one sectors per time, so loop over all sectors to read. -.sectors_loop: - call bd_read - cmp [hd_error], 0 - jnz .fail - mov ecx, [numsectors] - inc dword [ecx] ; one more sector is read - dec [sectors_todo] - jz .done - inc eax - jnz .sectors_loop -; 5. Loop is done, either due to error or because everything is done. -; Release the global lock and return the corresponding status. -.fail: - mov ecx, ide_mutex - call mutex_unlock - or eax, -1 - ret -.done: - mov ecx, ide_mutex - call mutex_unlock - xor eax, eax - ret -endp - -proc bd_write_interface stdcall uses esi edi, \ - userdata, buffer, startsector:qword, numsectors - ; userdata = old [hdpos] = 80h + index in NumBiosDisks - ; buffer = pointer to buffer with data - ; startsector = 64-bit start sector - ; numsectors = pointer to number of sectors on input, - ; must be filled with number of sectors really written -locals -sectors_todo dd ? -endl -; 1. Initialize number of sectors: get number of requested sectors -; and say that no sectors were read yet. - mov ecx, [numsectors] - mov eax, [ecx] - mov dword [ecx], 0 - mov [sectors_todo], eax -; 2. Acquire the global lock. - mov ecx, ide_mutex - call mutex_lock -; 3. Convert parameters to the form suitable for worker procedures. -; Underlying procedures do not know about 64-bit sectors. -; Worker procedures use global variables and esi for [buffer]. - cmp dword [startsector+4], 0 - jnz .fail - and [hd_error], 0 - mov eax, [userdata] - mov [hdpos], eax - mov esi, [buffer] - lea edi, [startsector] - mov [cache_chain_ptr], edi -; 4. Worker procedures take max 16 sectors per time, -; loop until all sectors will be processed. -.sectors_loop: - mov ecx, 16 - cmp ecx, [sectors_todo] - jbe @f - mov ecx, [sectors_todo] -@@: - mov [cache_chain_size], cl - call bd_write_cache_chain - cmp [hd_error], 0 - jnz .fail - movzx ecx, [cache_chain_size] - mov eax, [numsectors] - add [eax], ecx - sub [sectors_todo], ecx - jz .done - add [edi], ecx - jc .fail - shl ecx, 9 - add esi, ecx - jmp .sectors_loop -; 5. Loop is done, either due to error or because everything is done. -; Release the global lock and return the corresponding status. -.fail: - mov ecx, ide_mutex - call mutex_unlock - or eax, -1 - ret -.done: - mov ecx, ide_mutex - call mutex_unlock - xor eax, eax - ret -endp - -; This is a stub. -proc bd_querymedia stdcall, hd_data, mediainfo - mov eax, [mediainfo] - mov [eax+DISKMEDIAINFO.Flags], 0 - mov [eax+DISKMEDIAINFO.SectorSize], 512 - or dword [eax+DISKMEDIAINFO.Capacity], 0xFFFFFFFF - or dword [eax+DISKMEDIAINFO.Capacity+4], 0xFFFFFFFF - xor eax, eax - ret -endp - ;----------------------------------------------------------------------------- align 4 ; input: eax = sector, edi -> buffer @@ -803,23 +692,37 @@ IDE_irq_14_handler: pushfd cli pushad -; clear Bus Master IDE Command register mov [IDE_common_irq_param], 0 mov dx, [IDEContrRegsBaseAddr] - mov al, 0 - out dx, al +; test whether it is our interrupt? + add edx, 2 + in al, dx + test al, 100b + jz @f ; clear Bus Master IDE Status register ; clear Interrupt bit - add edx, 2 - mov al, 4 ; 100b out dx, al - +; clear Bus Master IDE Command register + sub edx, 2 + xor eax, eax + out dx, al +; read status register and remove the interrupt request + mov edx, [hdbase] + add edx, 0x7 + in al, dx + popad + popfd + mov al, 1 + ret +;-------------------------------------- +align 4 +@@: popad popfd ;-------------------------------------- align 4 .exit: - mov al, 1 + mov al, 0 ret ;----------------------------------------------------------------------------- align 4 @@ -830,24 +733,38 @@ IDE_irq_15_handler: pushfd cli pushad -; clear Bus Master IDE Command register mov [IDE_common_irq_param], 0 mov dx, [IDEContrRegsBaseAddr] add dx, 8 - mov al, 0 - out dx, al +; test whether it is our interrupt? + add edx, 2 + in al, dx + test al, 100b + jz @f ; clear Bus Master IDE Status register ; clear Interrupt bit - add edx, 2 - mov al, 4 ; 100b out dx, al - +; clear Bus Master IDE Command register + sub edx, 2 + mov al, 0 + out dx, al +; read status register and remove the interrupt request + mov edx, [hdbase] + add edx, 0x7 + in al, dx + popad + popfd + mov al, 1 + ret +;-------------------------------------- +align 4 +@@: popad popfd ;-------------------------------------- align 4 .exit: - mov al, 1 + mov al, 0 ret ;----------------------------------------------------------------------------- align 4 @@ -858,32 +775,46 @@ IDE_common_irq_handler: pushfd cli pushad -; clear Bus Master IDE Command register xor ebx, ebx mov dx, [IDEContrRegsBaseAddr] mov eax, IDE_common_irq_param cmp [eax], irq14_num mov [eax], bl - xor eax, eax je @f add dx, 8 ;-------------------------------------- align 4 @@: - out dx, al +; test whether it is our interrupt? + add edx, 2 + in al, dx + test al, 100b + jz @f ; clear Bus Master IDE Status register ; clear Interrupt bit - add edx, 2 - mov al, 4 ; 100b out dx, al - +; clear Bus Master IDE Command register + sub edx, 2 + xor eax, eax + out dx, al +; read status register and remove the interrupt request + mov edx, [hdbase] + add edx, 0x7 + in al, dx + popad + popfd + mov al, 1 + ret +;-------------------------------------- +align 4 +@@: popad popfd ;-------------------------------------- align 4 .exit: - mov al, 1 + mov al, 0 ret ;----------------------------------------------------------------------------- align 4 @@ -1252,139 +1183,3 @@ IDE_BAR2_val dw ? IDE_BAR3_val dw ? endg ;----------------------------------------------------------------------------- -; \begin{diamond} -uglobal -bios_hdpos dd 0 ; 0 is invalid value for [hdpos] -bios_cur_sector dd ? -bios_read_len dd ? -endg -;----------------------------------------------------------------------------- -align 4 -bd_read: - push eax - push edx - mov edx, [bios_hdpos] - cmp edx, [hdpos] - jne .notread - mov edx, [bios_cur_sector] - cmp eax, edx - jb .notread - add edx, [bios_read_len] - dec edx - cmp eax, edx - ja .notread - sub eax, [bios_cur_sector] - shl eax, 9 - add eax, (OS_BASE+0x9A000) - push ecx esi - mov esi, eax - mov ecx, 512/4 - cld - rep movsd - pop esi ecx - pop edx - pop eax - ret -.notread: - push ecx - mov dl, 42h - mov ecx, 16 - call int13_call - pop ecx - test eax, eax - jnz .v86err - test edx, edx - jz .readerr - mov [bios_read_len], edx - mov edx, [hdpos] - mov [bios_hdpos], edx - pop edx - pop eax - mov [bios_cur_sector], eax - jmp bd_read -.readerr: -.v86err: - mov [hd_error], 1 - jmp hd_read_error -;----------------------------------------------------------------------------- -align 4 -bd_write_cache_chain: - pusha - mov edi, OS_BASE + 0x9A000 - movzx ecx, [cache_chain_size] - push ecx - shl ecx, 9-2 - rep movsd - pop ecx - mov dl, 43h - mov eax, [cache_chain_ptr] - mov eax, [eax] - call int13_call - test eax, eax - jnz .v86err - cmp edx, ecx - jnz .writeerr - popa - ret -.v86err: -.writeerr: - popa - mov [hd_error], 1 - jmp hd_write_error -;----------------------------------------------------------------------------- -uglobal -int13_regs_in rb sizeof.v86_regs -int13_regs_out rb sizeof.v86_regs -endg -;----------------------------------------------------------------------------- -align 4 -int13_call: -; Because this code uses fixed addresses, -; it can not be run simultaniously by many threads. -; In current implementation it is protected by common mutex 'ide_status' - mov word [OS_BASE + 510h], 10h ; packet length - mov word [OS_BASE + 512h], cx ; number of sectors - mov dword [OS_BASE + 514h], 9A000000h ; buffer 9A00:0000 - mov dword [OS_BASE + 518h], eax - and dword [OS_BASE + 51Ch], 0 - push ebx ecx esi edi - mov ebx, int13_regs_in - mov edi, ebx - mov ecx, sizeof.v86_regs/4 - xor eax, eax - rep stosd - mov byte [ebx+v86_regs.eax+1], dl - mov eax, [hdpos] - lea eax, [BiosDisksData+(eax-80h)*4] - mov dl, [eax] - mov byte [ebx+v86_regs.edx], dl - movzx edx, byte [eax+1] -; mov dl, 5 - test edx, edx - jnz .hasirq - dec edx - jmp @f -.hasirq: - pushad - stdcall enable_irq, edx - popad -@@: - mov word [ebx+v86_regs.esi], 510h - mov word [ebx+v86_regs.ss], 9000h - mov word [ebx+v86_regs.esp], 0A000h - mov word [ebx+v86_regs.eip], 500h - mov [ebx+v86_regs.eflags], 20200h - mov esi, [sys_v86_machine] - mov ecx, 0x502 - push fs - call v86_start - pop fs - and [bios_hdpos], 0 - pop edi esi ecx ebx - movzx edx, byte [OS_BASE + 512h] - test byte [int13_regs_out+v86_regs.eflags], 1 - jnz @f - mov edx, ecx -@@: - ret -; \end{diamond} diff --git a/kernel/branches/Kolibri-acpi/boot/bootcode.inc b/kernel/branches/Kolibri-acpi/boot/bootcode.inc index 9f7d47079c..e58bee79a3 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootcode.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootcode.inc @@ -432,14 +432,14 @@ sayerr: mov [es:BOOT_IDE_PI_16], cx xor si, si ; device index = 0 int 0x1A - jnc .found + jnc .found_1 ; c) class 1, subclass 1, programming interface 0x85 mov ax, 0xB103 mov ecx, 1*10000h + 1*100h + 0x85 mov [es:BOOT_IDE_PI_16], cx xor si, si ; device index = 0 int 0x1A - jnc .found + jnc .found_1 ; d) class 1, subclass 1, programming interface 0x8A ; This is a Parallel IDE Controller which uses IRQs 14 and 15. mov ax, 0xB103 @@ -447,7 +447,7 @@ sayerr: mov [es:BOOT_IDE_PI_16], cx xor si, si ; device index = 0 int 0x1A - jnc .found ; Parallel IDE Controller + jnc .found_1 ; Parallel IDE Controller ; Controller not found! xor ax, ax mov [es:BOOT_IDE_PI_16], ax diff --git a/kernel/branches/Kolibri-acpi/bus/usb/common.inc b/kernel/branches/Kolibri-acpi/bus/usb/common.inc new file mode 100644 index 0000000000..d77e687a46 --- /dev/null +++ b/kernel/branches/Kolibri-acpi/bus/usb/common.inc @@ -0,0 +1,446 @@ +; Constants and structures that are shared between different parts of +; USB subsystem and *HCI drivers. + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; Version of all structures related to host controllers. +; Must be the same in kernel and *hci-drivers. +USBHC_VERSION = 1 + +; 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 + +; 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 + +; Possible speeds of USB devices +USB_SPEED_FS = 0 ; full-speed +USB_SPEED_LS = 1 ; low-speed +USB_SPEED_HS = 2 ; high-speed + +; 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 ================================= +; ============================================================================= + +; Description of controller-specific data and functions. +struct usb_hardware_func +Version dd ? ; must be USBHC_VERSION +ID dd ? ; '*HCI' +DataSize dd ? ; sizeof(*hci_controller) +BeforeInit dd ? +; Early initialization: take ownership from BIOS. +; in: [ebp-4] = (bus shl 8) + devfn +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 + +; pointers to kernel API functions that are called from *HCI-drivers +struct usbhc_func +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 dd ? +ends + +; 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. +PCICoordinates dd ? +; Device:function and bus number from PCI. +; +; 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. +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. +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 + +; 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 + +; 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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/ehci.inc b/kernel/branches/Kolibri-acpi/bus/usb/ehci.inc deleted file mode 100644 index f1dd991836..0000000000 --- a/kernel/branches/Kolibri-acpi/bus/usb/ehci.inc +++ /dev/null @@ -1,1895 +0,0 @@ -; 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. - -; ============================================================================= -; ================================= Constants ================================= -; ============================================================================= -; EHCI register declarations. -; Part 1. Capability registers. -; Base is MMIO from the PCI space. -EhciCapLengthReg = 0 -EhciVersionReg = 2 -EhciStructParamsReg = 4 -EhciCapParamsReg = 8 -EhciPortRouteReg = 0Ch -; Part 2. Operational registers. -; Base is (base for part 1) + (value of EhciCapLengthReg). -EhciCommandReg = 0 -EhciStatusReg = 4 -EhciInterruptReg = 8 -EhciFrameIndexReg = 0Ch -EhciCtrlDataSegReg = 10h -EhciPeriodicListReg = 14h -EhciAsyncListReg = 18h -EhciConfigFlagReg = 40h -EhciPortsReg = 44h - -; Possible values of ehci_pipe.NextQH.Type bitfield. -EHCI_TYPE_ITD = 0 ; isochronous transfer descriptor -EHCI_TYPE_QH = 1 ; queue head -EHCI_TYPE_SITD = 2 ; split-transaction isochronous TD -EHCI_TYPE_FSTN = 3 ; frame span traversal node - -; ============================================================================= -; ================================ Structures ================================= -; ============================================================================= - -; Hardware part of EHCI general transfer descriptor. -struct ehci_hardware_td -NextTD dd ? -; Bit 0 is Terminate bit, 1 = there is no next TD. -; Bits 1-4 must be zero. -; With masked 5 lower bits, this is the physical address of the next TD, if any. -AlternateNextTD dd ? -; Similar to NextTD, used if the transfer terminates with a short packet. -Token dd ? -; 1. Lower byte is Status field: -; bit 0 = ping state for USB2 endpoints, ERR handshake signal for USB1 endpoints -; bit 1 = split transaction state, meaningless for USB2 endpoints -; bit 2 = missed micro-frame -; bit 3 = transaction error -; bit 4 = babble detected -; bit 5 = data buffer error -; bit 6 = halted -; bit 7 = active -; 2. Next two bits (bits 8-9) are PID code, 0 = OUT, 1 = IN, 2 = SETUP. -; 3. Next two bits (bits 10-11) is ErrorCounter. Initialized as 3, decremented -; on each error; if it goes to zero, transaction is stopped. -; 4. Next 3 bits (bits 12-14) are CurrentPage field. -; 5. Next bit (bit 15) is InterruptOnComplete bit. -; 6. Next 15 bits (bits 16-30) are TransferLength field, -; number of bytes to transfer. -; 7. Upper bit (bit 31) is DataToggle bit. -BufferPointers rd 5 -; The buffer to be transferred can be spanned on up to 5 physical pages. -; The first item of this array is the physical address of the first byte in -; the buffer, other items are physical addresses of next pages. Lower 12 bits -; in other items must be set to zero; ehci_pipe.Overlay reuses some of them. -BufferPointersHigh rd 5 -; Upper dwords of BufferPointers for controllers with 64-bit memory access. -; Always zero. -ends - -; EHCI general transfer descriptor. -; * The structure describes transfers to be performed on Control, Bulk or -; Interrupt endpoints. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 52 bytes and corresponds to -; the Queue Element Transfer Descriptor from EHCI specification. -; * The hardware requires 32-bytes alignment of the hardware part, so -; the entire descriptor must be 32-bytes aligned. Since the allocator -; (usb_allocate_common) allocates memory sequentially from page start -; (aligned on 0x1000 bytes), block size for the allocator must be divisible -; by 32; ehci_alloc_td ensures this. -; * The hardware also requires that the hardware part must not cross page -; boundary; the allocator satisfies this automatically. -struct ehci_gtd ehci_hardware_td -Flags dd ? -; Copy of flags from the call to usb_*_transfer_async. -ends - -; EHCI-specific part of a pipe descriptor. -; * This structure corresponds to the Queue Head from the EHCI specification. -; * The hardware requires 32-bytes alignment of the hardware part. -; Since the allocator (usb_allocate_common) allocates memory sequentially -; from page start (aligned on 0x1000 bytes), block size for the allocator -; must be divisible by 32; ehci_alloc_pipe ensures this. -; * The hardware requires also that the hardware part must not cross page -; boundary; the allocator satisfies this automatically. -struct ehci_pipe -NextQH dd ? -; 1. First bit (bit 0) is Terminate bit, 1 = there is no next QH. -; 2. Next two bits (bits 1-2) are Type field of the next QH, -; one of EHCI_TYPE_* constants. -; 3. Next two bits (bits 3-4) are reserved, must be zero. -; 4. With masked 5 lower bits, this is the physical address of the next object -; to be processed, usually next QH. -Token dd ? -; 1. Lower 7 bits are DeviceAddress field. This is the address of the -; target device on the USB bus. -; 2. Next bit (bit 7) is Inactivate-on-next-transaction bit. Can be nonzero -; only for interrupt/isochronous USB1 endpoints. -; 3. Next 4 bits (bits 8-11) are Endpoint field. This is the target endpoint -; number. -; 4. Next 2 bits (bits 12-13) are EndpointSpeed field, one of EHCI_SPEED_*. -; 5. Next bit (bit 14) is DataToggleControl bit, -; 0 = use DataToggle bit from QH, 1 = from TD. -; 6. Next bit (bit 15) is Head-of-reclamation-list. The head of Control list -; has 1 here, all other QHs have zero. -; 7. Next 11 bits (bits 16-26) are MaximumPacketLength field for the target -; endpoint. -; 8. Next bit (bit 27) is ControlEndpoint bit, must be 1 for USB1 control -; endpoints and 0 for all others. -; 9. Upper 4 bits (bits 28-31) are NakCountReload field. -; Zero for USB1 endpoints, zero for periodic endpoints. -; For control/bulk USB2 endpoints, the code sets it to 4, -; which is rather arbitrary. -Flags dd ? -; 1. Lower byte is S-mask, each bit corresponds to one microframe per frame; -; bit is set <=> enable transactions in this microframe. -; 2. Next byte is C-mask, each bit corresponds to one microframe per frame; -; bit is set <=> enable complete-split transactions in this microframe. -; Meaningful only for USB1 endpoints. -; 3. Next 14 bits give address of the target device as hub:port, bits 16-22 -; are the USB address of the hub, bits 23-29 are the port number. -; Meaningful only for USB1 endpoints. -; 4. Upper 2 bits define number of consequetive transactions per micro-frame -; which host is allowed to permit for this endpoint. -; For control/bulk endpoints, it must be 1. -; For periodic endpoints, the value is taken from the endpoint descriptor. -HeadTD dd ? -; The physical address of the first TD for this pipe. -; Lower 5 bits must be zero. -Overlay ehci_hardware_td ? -; Working area for the current TD, if there is any. -; When TD is retired, it is written to that TD and Overlay is loaded -; from the new TD, if any. -BaseList dd ? -; Pointer to head of the corresponding pipe list. -ends - -; This structure describes the static head of every list of pipes. -; The hardware requires 32-bytes alignment of this structure. -; All instances of this structure are located sequentially in ehci_controller, -; ehci_controller is page-aligned, so it is sufficient to make this structure -; 32-bytes aligned and verify that the first instance is 32-bytes aligned -; inside ehci_controller. -; The hardware also requires that 44h bytes (size of 64-bit Queue Head -; Descriptor) starting at the beginning of this structure must not cross page -; boundary. If not, most hardware still behaves correctly (in fact, the last -; dword can have any value and this structure is never written), but on some -; hardware some things just break in mysterious ways. -struct ehci_static_ep -; Hardware fields are the same as in ehci_pipe. -; Only NextQH and Overlay.Token are actually used. -; NB: some emulators ignore Token.Halted bit (probably assuming that it is set -; only when device fails and emulation never fails) and always follow -; [Alternate]NextTD when they see that OverlayToken.Active bit is zero; -; so it is important to also set [Alternate]NextTD to 1. -NextQH dd ? -Token dd ? -Flags dd ? -HeadTD dd ? -NextTD dd ? -AlternateNextTD dd ? -OverlayToken dd ? -NextList dd ? -SoftwarePart rd sizeof.usb_static_ep/4 -Bandwidths rw 8 - dd ? -ends - -if sizeof.ehci_static_ep mod 32 -.err ehci_static_ep must be 32-bytes aligned -end if - -if ehci_static_ep.OverlayToken <> ehci_pipe.Overlay.Token -.err ehci_static_ep.OverlayToken misplaced -end if - -; EHCI-specific part of controller data. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 4096 bytes and corresponds to -; the Periodic Frame List from the EHCI specification. -; * The hardware requires page-alignment of the hardware part, so -; the entire descriptor must be page-aligned. -; This structure is allocated with kernel_alloc (see usb_init_controller), -; this gives page-aligned data. -; * The controller is described by both ehci_controller and usb_controller -; structures, for each controller there is one ehci_controller and one -; usb_controller structure. These structures are located sequentially -; in the memory: beginning from some page start, there is ehci_controller -; structure - this enforces hardware alignment requirements - and then -; usb_controller structure. -; * The code keeps pointer to usb_controller structure. The ehci_controller -; structure is addressed as [ptr + ehci_controller.field - sizeof.ehci_controller]. -struct ehci_controller -; ------------------------------ hardware fields ------------------------------ -FrameList rd 1024 -; Entry n corresponds to the head of the frame list to be executed in -; the frames n,n+1024,n+2048,n+3072,... -; The first bit of each entry is Terminate bit, 1 = the frame is empty. -; Bits 1-2 are Type field, one of EHCI_TYPE_* constants. -; Bits 3-4 must be zero. -; With masked 5 lower bits, the entry is a physical address of the first QH/TD -; to be executed. -; ------------------------------ software fields ------------------------------ -; Every list has the static head, which is an always halted QH. -; The following fields are static heads, one per list: -; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list. -IntEDs ehci_static_ep - rb 62 * sizeof.ehci_static_ep -; Beware. -; Two following strings ensure that 44h bytes at any static head -; do not cross page boundary. Without that, the code "works on my machine"... -; but fails on some hardware in seemingly unrelated ways. -; One hardware TD (without any software fields) fit in the rest of the page. -ehci_controller.ControlDelta = 2000h - (ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) -StopQueueTD ehci_hardware_td -; Used as AlternateNextTD for transfers when short packet is considered -; as an error; short packet must stop the queue in this case, not advance -; to the next transfer. - rb ehci_controller.ControlDelta - sizeof.ehci_hardware_td -; Padding for page-alignment. -ControlED ehci_static_ep -BulkED ehci_static_ep -MMIOBase1 dd ? -; Virtual address of memory-mapped area with part 1 of EHCI registers EhciXxxReg. -MMIOBase2 dd ? -; Pointer inside memory-mapped area MMIOBase1; points to part 2 of EHCI registers. -StructuralParams dd ? -; Copy of EhciStructParamsReg value. -CapabilityParams dd ? -; Copy of EhciCapParamsReg value. -DeferredActions dd ? -; Bitmask of events from EhciStatusReg which were observed by the IRQ handler -; and needs to be processed in the IRQ thread. -ends - -if ehci_controller.IntEDs mod 32 -.err Static endpoint descriptors must be 32-bytes aligned inside ehci_controller -end if - -; Description of #HCI-specific data and functions for -; controller-independent code. -; Implements the structure usb_hardware_func from hccommon.inc for EHCI. -iglobal -align 4 -ehci_hardware_func: - dd 'EHCI' - dd sizeof.ehci_controller - dd ehci_init - dd ehci_process_deferred - dd ehci_set_device_address - dd ehci_get_device_address - dd ehci_port_disable - dd ehci_new_port.reset - dd ehci_set_endpoint_packet_size - dd ehci_alloc_pipe - dd ehci_free_pipe - dd ehci_init_pipe - dd ehci_unlink_pipe - dd ehci_alloc_td - dd ehci_free_td - dd ehci_alloc_transfer - dd ehci_insert_transfer - dd ehci_new_device -endg - -; ============================================================================= -; =================================== Code ==================================== -; ============================================================================= - -; Controller-specific initialization function. -; Called from usb_init_controller. Initializes the hardware and -; EHCI-specific parts of software structures. -; eax = pointer to ehci_controller to be initialized -; [ebp-4] = pcidevice -proc ehci_init -; inherit some variables from the parent (usb_init_controller) -.devfn equ ebp - 4 -.bus equ ebp - 3 -; 1. Store pointer to ehci_controller for further use. - push eax - mov edi, eax - mov esi, eax -; 2. Initialize ehci_controller.FrameList. -; Note that FrameList is located in the beginning of ehci_controller, -; so esi and edi now point to ehci_controller.FrameList. -; First 32 entries of FrameList contain physical addresses -; of first 32 Periodic static heads, further entries duplicate these. -; See the description of structures for full info. -; 2a. Get physical address of first static head. -; Note that 1) it is located in the beginning of a page -; and 2) first 32 static heads fit in the same page, -; so one call to get_phys_addr without correction of lower 12 bits -; is sufficient. -if (ehci_controller.IntEDs / 0x1000) <> ((ehci_controller.IntEDs + 32 * sizeof.ehci_static_ep) / 0x1000) -.err assertion failed -end if -if (ehci_controller.IntEDs mod 0x1000) <> 0 -.err assertion failed -end if - add eax, ehci_controller.IntEDs - call get_phys_addr -; 2b. Fill first 32 entries. - inc eax - inc eax ; set Type to EHCI_TYPE_QH - movi ecx, 32 - mov edx, ecx -@@: - stosd - add eax, sizeof.ehci_static_ep - loop @b -; 2c. Fill the rest entries. - mov ecx, 1024 - 32 - rep movsd -; 3. Initialize static heads ehci_controller.*ED. -; Use the loop over groups: first group consists of first 32 Periodic -; descriptors, next group consists of next 16 Periodic descriptors, -; ..., last group consists of the last Periodic descriptor. -; 3a. Prepare for the loop. -; make esi point to the second group, other registers are already set. - add esi, 32*4 + 32*sizeof.ehci_static_ep -; 3b. Loop over groups. On every iteration: -; edx = size of group, edi = pointer to the current group, -; esi = pointer to the next group. -.init_static_eds: -; 3c. Get the size of next group. - shr edx, 1 -; 3d. Exit the loop if there is no next group. - jz .init_static_eds_done -; 3e. Initialize the first half of the current group. -; Advance edi to the second half. - push esi - call ehci_init_static_ep_group - pop esi -; 3f. Initialize the second half of the current group -; with the same values. -; Advance edi to the next group, esi/eax to the next of the next group. - call ehci_init_static_ep_group - jmp .init_static_eds -.init_static_eds_done: -; 3g. Initialize the last static head. - xor esi, esi - call ehci_init_static_endpoint -; While we are here, initialize StopQueueTD. -if (ehci_controller.StopQueueTD <> ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) -.err assertion failed -end if - inc [edi+ehci_hardware_td.NextTD] ; 0 -> 1 - inc [edi+ehci_hardware_td.AlternateNextTD] ; 0 -> 1 -; leave other fields as zero, including Active bit -; 3i. Initialize the head of Control list. - add edi, ehci_controller.ControlDelta - lea esi, [edi+sizeof.ehci_static_ep] - call ehci_init_static_endpoint - or byte [edi-sizeof.ehci_static_ep+ehci_static_ep.Token+1], 80h -; 3j. Initialize the head of Bulk list. - sub esi, sizeof.ehci_static_ep - 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 - or al, 6 - xchg eax, ecx - call pci_write_reg -; 4b. Read memory base address. - mov ah, [.bus] - mov al, 2 - mov bl, 10h - call pci_read_reg -; 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 - test eax, eax - jz .fail -; DEBUGF 1,'K : MMIO %x\n',eax -if ehci_controller.MMIOBase1 <> ehci_controller.BulkED + sizeof.ehci_static_ep -.err assertion failed -end if - stosd ; fill ehci_controller.MMIOBase1 - 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 -end if - stosd ; fill ehci_controller.MMIOBase2 -if ehci_controller.StructuralParams <> ehci_controller.MMIOBase2 + 4 -.err assertion failed -end if -if ehci_controller.CapabilityParams <> ehci_controller.StructuralParams + 4 -.err assertion failed -end if - mov [edi], ebx ; fill ehci_controller.StructuralParams - mov [edi+4], edx ; fill ehci_controller.CapabilityParams - DEBUGF 1,'K : HCSPARAMS=%x, HCCPARAMS=%x\n',ebx,edx - and ebx, 15 - mov [edi+usb_controller.NumPorts+sizeof.ehci_controller-ehci_controller.StructuralParams], ebx - mov edi, eax -; now edi = MMIOBase2 -; 6. Transfer the controller to a known state. -; 6b. Stop the controller if it is running. - movi ecx, 10 - test dword [edi+EhciStatusReg], 1 shl 12 - jnz .stopped - and dword [edi+EhciCommandReg], not 1 -@@: - movi esi, 1 - call delay_ms - test dword [edi+EhciStatusReg], 1 shl 12 - jnz .stopped - loop @b - dbgstr 'Failed to stop EHCI controller' - jmp .fail_unmap -.stopped: -; 6c. Reset the controller. Wait up to 50 ms checking status every 1 ms. - or dword [edi+EhciCommandReg], 2 - movi ecx, 50 -@@: - movi esi, 1 - call delay_ms - test dword [edi+EhciCommandReg], 2 - jz .reset_ok - loop @b - dbgstr 'Failed to reset EHCI controller' - jmp .fail_unmap -.reset_ok: -; 7. Configure the controller. - pop esi ; restore the pointer saved at step 1 - add esi, sizeof.ehci_controller -; 7a. If the controller is 64-bit, say to it that all structures are located -; in first 4G. - test byte [esi+ehci_controller.CapabilityParams-sizeof.ehci_controller], 1 - jz @f - 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 -; al = IRQ - DEBUGF 1,'K : attaching to IRQ %x\n',al - movzx eax, al - stdcall attach_int_handler, 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 - 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 - mov dword [edi+EhciAsyncListReg], eax -; 7e. Configure operational details and run the controller. - mov dword [edi+EhciCommandReg], \ - (1 shl 16) + \ ; interrupt threshold = 1 microframe = 0.125ms - (0 shl 11) + \ ; disable Async Park Mode - (0 shl 8) + \ ; zero Async Park Mode Count - (1 shl 5) + \ ; Async Schedule Enable - (1 shl 4) + \ ; Periodic Schedule Enable - (0 shl 2) + \ ; 1024 elements in FrameList - 1 ; Run -; 7f. Route all ports to this controller, not companion controllers. - mov dword [edi+EhciConfigFlagReg], 1 - DEBUGF 1,'K : EHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] -; 8. Apply port power, if needed, and disable all ports. - xor ecx, ecx -@@: - mov dword [edi+EhciPortsReg+ecx*4], 1000h ; Port Power enabled, all other bits disabled - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb @b - test byte [esi+ehci_controller.StructuralParams-sizeof.ehci_controller], 10h - jz @f - push esi - movi esi, 20 - call delay_ms - pop esi -@@: -; 9. Return pointer to usb_controller. - xchg eax, esi - ret -; 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. -.fail_unmap: - pop eax - push eax - stdcall free_kernel_space, [eax+ehci_controller.MMIOBase1] -.fail: - pop ecx - xor eax, eax - ret -endp - -; Helper procedure for step 3 of ehci_init, see comments there. -; Initializes the static head of one list. -; esi = pointer to the "next" list, edi = pointer to head to initialize. -; Advances edi to the next head, keeps esi. -proc ehci_init_static_endpoint - xor eax, eax - inc eax ; set Terminate bit - mov [edi+ehci_static_ep.NextTD], eax - mov [edi+ehci_static_ep.AlternateNextTD], eax - test esi, esi - jz @f - mov eax, esi - call get_phys_addr - inc eax - inc eax ; set Type to EHCI_TYPE_QH -@@: - mov [edi+ehci_static_ep.NextQH], eax - 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 - add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart - ret -endp - -; Helper procedure for step 3 of ehci_init, see comments there. -; Initializes one half of group of static heads. -; edx = size of the next group = half of size of the group, -; edi = pointer to the group, esi = pointer to the next group. -; Advances esi, edi to next group, keeps edx. -proc ehci_init_static_ep_group - push edx -@@: - call ehci_init_static_endpoint - add esi, sizeof.ehci_static_ep - dec edx - jnz @b - pop edx - ret -endp - -; Controller-specific pre-initialization function: take ownership from BIOS. -; Some BIOSes, although not all of them, use USB controllers themselves -; to support USB flash drives. In this case, -; we must notify the BIOS that we don't need that emulation and know how to -; 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 - 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 - test eax, eax - jz .nothing - push eax ; push argument for step 8 -; 3. Some BIOSes enable controller interrupts as a result of giving -; controller away. At this point the system knows nothing about how to serve -; EHCI interrupts, so such an interrupt will send the system into an infinite -; loop handling the same IRQ again and again. Thus, we need to block EHCI -; interrupts. We can't do this at the controller level until step 5, -; because the controller is currently owned by BIOS, so we block all hardware -; interrupts on this processor until step 5. - pushf - cli -; 4. Take the ownership over the controller. -; 4a. Locate take-ownership capability in the PCI configuration space. -; Limit the loop with 100h iterations; since the entire configuration space is -; 100h bytes long, hitting this number of iterations means that something is -; corrupted. -; Use a value from MMIO as a starting point. - mov edx, [eax+EhciCapParamsReg] - DEBUGF 1,'K : edx=%x\n',edx - movzx edi, byte [eax+EhciCapLengthReg] - add edi, eax - push 0 - mov bl, dh ; get Extended Capabilities Pointer - test bl, bl - jz .has_ownership2 - cmp bl, 40h - jb .no_capability -.look_bios_handoff: - 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 -; check, whether the capability ID is take-ownership ID = 1, - cmp al, 1 - jz .found_bios_handoff -; if not, advance to next-capability link and continue loop. - dec byte [esp] - jz .no_capability - mov bl, ah - cmp bl, 40h - jae .look_bios_handoff -.no_capability: - dbgstr 'warning: cannot locate take-ownership capability' - jmp .has_ownership2 -.found_bios_handoff: -; 4b. Check whether BIOS has ownership. -; Some BIOSes release ownership before loading OS, but forget to unwatch for -; change-ownership requests; they cannot handle ownership request, so -; such a request sends the system into infinite loop of handling the same SMI -; over and over. Avoid this. - inc ebx - inc ebx - test eax, 0x10000 - jz .has_ownership -; 4c. Request ownership. - inc ebx - mov cl, 1 - mov ah, [esi+PCIDEV.bus] - mov al, 0 - call pci_write_reg -; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership -; requests; if so, there is no sense in waiting. - inc ebx - mov ah, [esi+PCIDEV.bus] - mov al, 2 - call pci_read_reg - dec ebx - dec ebx - test ah, 20h - jz .force_ownership -; 4e. Wait for result no more than 1 s, checking for status every 1 ms. -; If successful, go to 5. - mov dword [esp], 1000 -@@: - mov ah, [esi+PCIDEV.bus] - mov al, 0 - call pci_read_reg - test al, 1 - jz .has_ownership - push esi - movi esi, 1 - call delay_ms - pop esi - dec dword [esp] - jnz @b - dbgstr 'warning: taking EHCI ownership from BIOS timeout' -.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 -.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 - and ax, 2000h - xchg eax, ecx - call pci_write_reg -.has_ownership2: - pop ecx -; 6. Disable all controller interrupts until the system will be ready to -; process them. - mov dword [edi+EhciInterruptReg], 0 -; 7. Now we can unblock interrupts in the processor. - popf -; 8. Release memory mapping created in step 2 and return. - call free_kernel_space -.nothing: - ret -endp - -; IRQ handler for EHCI controllers. -ehci_irq.noint: - spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] -; Not our interrupt: restore registers and return zero. - xor eax, eax - pop edi esi ebx - ret - -proc ehci_irq - push ebx esi edi ; save registers to be cdecl -virtual at esp - rd 3 ; saved registers - dd ? ; return address -.controller dd ? -end virtual -; 1. ebx will hold whether some deferred processing is needed, -; that cannot be done from the interrupt handler. Initialize to zero. - xor ebx, ebx -; 2. Get the mask of events which should be processed. - mov esi, [.controller] - 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] - jz .noint -; 4. Clear the events we know of. -; Note that this should be done before processing of events: -; new events could arise while we are processing those, this way we won't lose -; them (the controller would generate another interrupt after completion -; of this one). -; DEBUGF 1,'K : EHCI interrupt: status = %x\n',eax - mov [edi+EhciStatusReg], eax -; 5. Sanity check. - test al, 10h - jz @f - DEBUGF 1,'K : something terrible happened with EHCI %x (%x)\n',esi,al -@@: -; We can't do too much from an interrupt handler. Inform the processing thread -; that it should perform appropriate actions. - or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax - spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] - inc ebx - call usb_wakeup_if_needed -; 6. Interrupt processed; return non-zero. - mov al, 1 - pop edi esi ebx ; restore used registers to be cdecl - ret -endp - -; This procedure is called from usb_set_address_callback -; and stores USB device address in the ehci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe, cl = address -proc ehci_set_device_address - mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl - call usb_subscribe_control - ret -endp - -; This procedure returns USB device address from the ehci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe -; out: eax = endpoint address -proc ehci_get_device_address - mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] - and eax, 7Fh - ret -endp - -; This procedure is called from usb_set_address_callback -; if the device does not accept SET_ADDRESS command and needs -; to be disabled at the port level. -; in: esi -> usb_controller, ecx = port (zero-based) -proc ehci_port_disable - mov eax, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] - and dword [eax+EhciPortsReg+ecx*4], not (4 or 2Ah) - ret -endp - -; This procedure is called from usb_get_descr8_callback when -; the packet size for zero endpoint becomes known and -; stores the packet size in ehci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size -proc ehci_set_endpoint_packet_size - mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] - and eax, not (0x7FF shl 16) - shl ecx, 16 - or eax, ecx - mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax -; Wait until hardware cache is evicted. - call usb_subscribe_control - ret -endp - -uglobal -align 4 -; Data for memory allocator, see memory.inc. -ehci_ep_first_page dd ? -ehci_ep_mutex MUTEX -ehci_gtd_first_page dd ? -ehci_gtd_mutex MUTEX -endg - -; This procedure allocates memory for pipe. -; Both hardware+software parts must be allocated, returns pointer to usb_pipe -; (software part). -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 - test eax, eax - jz @f - add eax, sizeof.ehci_pipe -@@: - pop ebx - ret -endp - -; This procedure frees memory for pipe allocated by ehci_alloc_pipe. -; void stdcall with one argument = pointer to usb_pipe. -proc ehci_free_pipe -virtual at esp - dd ? ; return address -.ptr dd ? -end virtual - sub [.ptr], sizeof.ehci_pipe - jmp usb_free_common -endp - -; This procedure is called from API usb_open_pipe and processes -; the controller-specific part of this API. See docs. -; 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 -proc ehci_init_pipe -virtual at ebp+8 -.config_pipe dd ? -.endpoint dd ? -.maxpacket dd ? -.type dd ? -.interval dd ? -end virtual -; 1. Zero all fields in the hardware part. - push eax ecx - sub edi, sizeof.ehci_pipe - xor eax, eax - movi ecx, sizeof.ehci_pipe/4 - rep stosd - pop ecx eax -; 2. Setup PID in the first TD and make sure that the it is not active. - xor edx, edx - test byte [.endpoint], 80h - setnz dh - mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx - mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 - mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 -; 3. Store physical address of the first TD. - sub eax, sizeof.ehci_gtd - call get_phys_addr - mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax -; 4. Fill ehci_pipe.Flags except for S- and C-masks. -; Copy location from the config pipe. - mov eax, [ecx+ehci_pipe.Flags-sizeof.ehci_pipe] - and eax, 3FFF0000h -; Use 1 requests per microframe for control/bulk endpoints, -; use value from the endpoint descriptor for periodic endpoints - movi edx, 1 - test [.type], 1 - jz @f - mov edx, [.maxpacket] - shr edx, 11 - inc edx -@@: - shl edx, 30 - or eax, edx - mov [edi+ehci_pipe.Flags-sizeof.ehci_pipe], eax -; 5. Fill ehci_pipe.Token. - mov eax, [ecx+ehci_pipe.Token-sizeof.ehci_pipe] -; copy following fields from the config pipe: -; DeviceAddress, EndpointSpeed, ControlEndpoint if new type is control - mov ecx, eax - and eax, 307Fh - and ecx, 8000000h - or ecx, 4000h - mov edx, [.endpoint] - and edx, 15 - shl edx, 8 - or eax, edx - mov edx, [.maxpacket] - shl edx, 16 - or eax, edx -; for control endpoints, use DataToggle from TD, otherwise use DataToggle from QH - cmp [.type], CONTROL_PIPE - jnz @f - or eax, ecx -@@: -; for control/bulk USB2 endpoints, set NakCountReload to 4 - test eax, USB_SPEED_HS shl 12 - jz .nonak - cmp [.type], CONTROL_PIPE - jz @f - cmp [.type], BULK_PIPE - jnz .nonak -@@: - or eax, 40000000h -.nonak: - mov [edi+ehci_pipe.Token-sizeof.ehci_pipe], eax -; 5. Select the corresponding list and insert to the list. -; 5a. Use Control list for control pipes, Bulk list for bulk pipes. - lea edx, [esi+ehci_controller.ControlED.SoftwarePart-sizeof.ehci_controller] - cmp [.type], BULK_PIPE - jb .insert ; control pipe - lea edx, [esi+ehci_controller.BulkED.SoftwarePart-sizeof.ehci_controller] - jz .insert ; bulk pipe -.interrupt_pipe: -; 5b. For interrupt pipes, let the scheduler select the appropriate list -; and the appropriate microframe(s) (which goes to S-mask and C-mask) -; based on the current bandwidth distribution and the requested bandwidth. -; There are two schedulers, one for high-speed devices, -; another for split transactions. -; This could fail if the requested bandwidth is not available; -; if so, return an error. - test word [edi+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh - jnz .interrupt_tt - call ehci_select_hs_interrupt_list - jmp .interrupt_common -.interrupt_tt: - call ehci_select_tt_interrupt_list -.interrupt_common: - test edx, edx - jz .return0 - mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax -.insert: - mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx -; Insert to the head of the corresponding list. -; Note: inserting to the head guarantees that the list traverse in -; ehci_process_updated_schedule, once started, will not interact with new pipes. -; However, we still need to ensure that links in the new pipe (edi.NextVirt) -; are initialized before links to the new pipe (edx.NextVirt). -; 5c. Insert in the list of virtual addresses. - mov ecx, [edx+usb_pipe.NextVirt] - mov [edi+usb_pipe.NextVirt], ecx - mov [edi+usb_pipe.PrevVirt], edx - mov [ecx+usb_pipe.PrevVirt], edi - mov [edx+usb_pipe.NextVirt], edi -; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, -; store the physical address of the new pipe to previous NextQH. - mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] - mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx - lea eax, [edi-sizeof.ehci_pipe] - call get_phys_addr - inc eax - inc eax - mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax -; 6. Return with nonzero eax. - ret -.return0: - xor eax, eax - ret -endp - -; This function is called from ehci_process_deferred when -; a new device was connected at least USB_CONNECT_DELAY ticks -; and therefore is ready to be configured. -; ecx = port, esi -> ehci_controller, edi -> EHCI MMIO -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 - mov edx, eax - and ah, 0Ch - cmp ah, 4 - jz .low_speed -; 2. Devices operating at full-speed and high-speed must now have ah == 8. -; Some broken hardware asserts both D+ and D- even after initial decoupling; -; if so, stop initialization here, no sense in further actions. - cmp ah, 0Ch - jz .se1 -; 3. If another port is resetting right now, mark this port as 'reset pending' -; and return. - bts [esi+usb_controller.PendingPorts], ecx - cmp [esi+usb_controller.ResettingPort], -1 - jnz .nothing - btr [esi+usb_controller.PendingPorts], ecx -; Otherwise, fall through to ohci_new_port.reset. - -; This function is called from ehci_new_port and usb_test_pending_port. -; It starts reset signalling for the port. Note that in USB first stages -; of configuration can not be done for several ports in parallel. -.reset: - push edi - mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] - mov eax, [edi+EhciPortsReg+ecx*4] -; 1. Store information about resetting hub (roothub) and port. - and [esi+usb_controller.ResettingHub], 0 - mov [esi+usb_controller.ResettingPort], cl -; 2. Initiate reset signalling. - or ah, 1 - 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] - 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 - 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 - or dh, 20h - and dl, not 2Ah - mov [edi+EhciPortsReg+ecx*4], edx - ret -.se1: - dbgstr 'SE1 after connect debounce. Broken hardware?' - ret -endp - -; This procedure is called from several places in main USB code -; and allocates required packets for the given transfer. -; ebx = 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 -proc ehci_alloc_transfer stdcall uses edi, \ - buffer:dword, size:dword, flags:dword, td:dword, direction:dword -locals -origTD dd ? -packetSize dd ? ; must be last variable, see usb_init_transfer -endl -; 1. Save original value of td: -; it will be useful for rollback if something would fail. - mov eax, [td] - mov [origTD], eax -; One transfer descriptor can describe up to 5 pages. -; In the worst case (when the buffer is something*1000h+0FFFh) -; this corresponds to 4001h bytes. If the requested size is -; greater, we should split the transfer into several descriptors. -; Boundaries to split must be multiples of endpoint transfer size -; to avoid short packets except in the end of the transfer. - cmp [size], 4001h - jbe .lastpacket -; 2. While the remaining data cannot fit in one descriptor, -; 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] - mov eax, 4001h - xor edx, edx - mov edi, eax - div ecx - sub edi, edx - mov [packetSize], edi -.fullpackets: - call ehci_alloc_packet - test eax, eax - jz .fail - mov [td], eax - add [buffer], edi - sub [size], edi - cmp [size], 4001h - ja .fullpackets -; 3. The remaining data can fit in one packet; -; allocate the last descriptor with size = size of remaining data. -.lastpacket: - mov eax, [size] - mov [packetSize], eax - call ehci_alloc_packet - test eax, eax - jz .fail -; 9. Update flags in the last packet. - mov edx, [flags] - mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], edx -; 10. Fill AlternateNextTD field in all allocated TDs. -; If the caller says that short transfer is ok, the queue must advance to -; the next descriptor, which is in eax. -; Otherwise, the queue should stop, so make AlternateNextTD point to -; always-inactive descriptor StopQueueTD. - push eax - test dl, 1 - jz .disable_short - sub eax, sizeof.ehci_gtd - jmp @f -.disable_short: - mov eax, [ebx+usb_pipe.Controller] - add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller -@@: - call get_phys_addr - mov edx, [origTD] -@@: - cmp edx, [esp] - jz @f - mov [edx+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], eax - mov edx, [edx+usb_gtd.NextVirt] - jmp @b -@@: - pop eax - ret -.fail: - mov edi, ehci_hardware_func - mov eax, [td] - stdcall usb_undo_tds, [origTD] - xor eax, eax - ret -endp - -; Helper procedure for ehci_alloc_transfer. -; Allocates and initializes one transfer descriptor. -; ebx = pipe, other parameters are passed through the stack; -; fills the current last descriptor and -; returns eax = next descriptor (not filled). -proc ehci_alloc_packet -; inherit some variables from the parent ehci_alloc_transfer -virtual at ebp-8 -.origTD dd ? -.packetSize dd ? - rd 2 -.buffer dd ? -.transferSize dd ? -.Flags dd ? -.td dd ? -.direction dd ? -end virtual -; 1. Allocate the next TD. - call ehci_alloc_td - test eax, eax - jz .nothing -; 2. Initialize controller-independent parts of both TDs. - push eax - call usb_init_transfer - pop eax -; 3. Copy PID to the new descriptor. - mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] - mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx - mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 - mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 -; 4. Save the returned value (next descriptor). - push eax -; 5. Store the physical address of the next descriptor. - sub eax, sizeof.ehci_gtd - call get_phys_addr - mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax -; 6. For zero-length transfers, store zero in all fields for buffer addresses. -; Otherwise, fill them with real values. - xor eax, eax - mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], eax -repeat 10 - mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd+(%-1)*4], eax -end repeat - cmp [.packetSize], eax - jz @f - mov eax, [.buffer] - call get_phys_addr - mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax - and eax, 0xFFF - mov edx, [.packetSize] - add edx, eax - sub edx, 0x1000 - jbe @f - mov eax, [.buffer] - add eax, 0x1000 - call get_pg_addr - mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax - sub edx, 0x1000 - jbe @f - mov eax, [.buffer] - add eax, 0x2000 - call get_pg_addr - mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax - sub edx, 0x1000 - jbe @f - mov eax, [.buffer] - add eax, 0x3000 - call get_pg_addr - mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax - sub edx, 0x1000 - jbe @f - mov eax, [.buffer] - add eax, 0x4000 - call get_pg_addr - mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax -@@: -; 7. Fill Token field: -; set Status = 0 (inactive, ehci_insert_transfer would mark everything active); -; keep current PID if [.direction] is zero, use two lower bits of [.direction] -; otherwise shifted as (0|1|2) -> (2|0|1); -; set error counter to 3; -; set current page to 0; -; do not interrupt on complete (ehci_insert_transfer sets this bit where needed); -; set DataToggle to bit 2 of [.direction]. - mov eax, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] - and eax, 300h ; keep PID code - mov edx, [.direction] - test edx, edx - jz .haspid - and edx, 3 - dec edx - jns @f - add edx, 3 -@@: - mov ah, dl - mov edx, [.direction] - and edx, not 3 - shl edx, 29 - or eax, edx -.haspid: - or eax, 0C00h - mov edx, [.packetSize] - shl edx, 16 - or eax, edx - mov [ecx+ehci_gtd.Token-sizeof.ehci_gtd], eax -; 4. Restore the returned value saved in step 2. - pop eax -.nothing: - ret -endp - -; This procedure is called from several places in main USB code -; and activates the transfer which was previously allocated by -; ehci_alloc_transfer. -; ecx -> last descriptor for the transfer, ebx -> usb_pipe -proc ehci_insert_transfer - or byte [ecx+ehci_gtd.Token+1-sizeof.ehci_gtd], 80h ; set IOC bit - mov eax, [esp+4] -.activate: - or byte [eax+ehci_gtd.Token-sizeof.ehci_gtd], 80h ; set Active bit - cmp eax, ecx - mov eax, [eax+usb_gtd.NextVirt] - jnz .activate - ret -endp - -; This function is called from ehci_process_deferred when -; reset signalling for a new device needs to be finished. -proc ehci_port_reset_done - movzx ecx, [esi+usb_controller.ResettingPort] - and dword [edi+EhciPortsReg+ecx*4], not 12Ah - mov eax, [timer_ticks] - 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 - ret -endp - -; This function is called from ehci_process_deferred when -; a new device has been reset, recovered after reset and needs to be configured. -proc ehci_port_init -; 1. Get the status and set it to zero. -; If reset has been failed (device disconnected during reset), -; continue to next device (if there is one). - xor eax, eax - xchg al, [esi+usb_controller.ResettingStatus] - test al, al - js 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 - 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 -@@: -; 3. Call the worker procedure to notify the protocol layer -; about new EHCI device. It is high-speed. - movi eax, USB_SPEED_HS - call ehci_new_device - test eax, eax - jnz .nothing -; 4. If something at the protocol layer has failed -; (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 -.nothing: - ret -endp - -; This procedure is called from ehci_port_init and from hub support code -; when a new device is connected and has been reset. -; It calls usb_new_device at the protocol layer with correct parameters. -; in: esi -> usb_controller, eax = speed. -proc ehci_new_device - push ebx ecx ; save used registers (ecx is important for ehci_port_init) -; 1. Store the speed for the protocol layer. - mov [esi+usb_controller.ResettingSpeed], al -; 2. Shift speed bits to the proper place in ehci_pipe.Token. - shl eax, 12 -; 3. For high-speed devices, go to step 5 with edx = 0. - xor edx, edx - cmp ah, USB_SPEED_HS shl (12-8) - jz .common -; 4. For low-speed and full-speed devices, fill address:port -; 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. - 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] - inc ecx - mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] - shl ecx, 23 - and edx, 7Fh - shl edx, 16 - or edx, ecx ; ehci_pipe.Flags - pop eax - or eax, 1 shl 27 ; ehci_pipe.Token -.common: -; 5. Create pseudo-pipe in the stack. -; See ehci_init_pipe: only .Controller, .Token, .Flags fields are used. - push esi ; usb_pipe.Controller - mov ecx, esp - sub esp, sizeof.ehci_pipe - ehci_pipe.Flags - 4 - push edx ; ehci_pipe.Flags - push eax ; ehci_pipe.Token -; 6. Notify the protocol layer. - call usb_new_device -; 7. Cleanup the stack after step 5 and return. - add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 - pop ecx ebx ; restore used registers - ret -endp - -; This procedure is called in the USB thread from usb_thread_proc, -; processes regular actions and those actions which can't be safely done -; from interrupt handler. -; Returns maximal time delta before the next call. -proc ehci_process_deferred - push ebx edi ; save used registers to be stdcall - mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] -; 1. Get the mask of events to process. - xor eax, eax - xchg eax, [esi+ehci_controller.DeferredActions-sizeof.ehci_controller] - push eax -; 2. Initialize the return value. - push -1 -; Handle roothub events. -; 3a. Test whether there are such events. - test al, 4 - jz .skip_roothub -; Status of some port has changed. Loop over all ports. -; 3b. Prepare for the loop: start from port 0. - xor ecx, ecx -.portloop: -; 3c. Get the port status and changes of it. -; If there are no changes, just continue to the next port. - mov eax, [edi+EhciPortsReg+ecx*4] - test al, 2Ah - jz .nextport -; 3d. Clear change bits and read the status again. -; (It is possible, although quite unlikely, that some event occurs between -; the first read and the clearing, invalidating the old status. If an event -; occurs after the clearing, we will not miss it, looking in the next scan. - 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 -; 3e. Handle overcurrent. -; Note: that needs work. - test bl, 20h ; overcurrent change - jz .noovercurrent - test al, 10h ; overcurrent active - jz .noovercurrent - DEBUGF 1,'K : overcurrent at port %d\n',ecx -.noovercurrent: -; 3f. Handle changing of connection status. - test bl, 2 - jz .nocsc -; There was a connect or disconnect event at this port. -; 3g. Disconnect the old device on this port, if any. -; If the port was resetting, indicate fail; later stages will process it. - cmp [esi+usb_controller.ResettingHub], 0 - jnz @f - cmp cl, [esi+usb_controller.ResettingPort] - jnz @f - mov [esi+usb_controller.ResettingStatus], -1 -@@: - bts [esi+usb_controller.NewDisconnected], ecx -; 3h. Change connected status. For the connection event, also store -; the connection time; any further processing is permitted only after -; USB_CONNECT_DELAY ticks. - test al, 1 - jz .disconnect - mov eax, [timer_ticks] - mov [esi+usb_controller.ConnectedTime+ecx*4], eax - bts [esi+usb_controller.NewConnected], ecx - jmp .nextport -.disconnect: - btr [esi+usb_controller.NewConnected], ecx - jmp .nextport -.nocsc: -; 3i. Handle port disabling. -; Note: that needs work. - test al, 8 - jz @f - test al, 4 - jz @f - DEBUGF 1,'K : port %d disabled\n',ecx -@@: -; 3j. Continue the loop for the next port. -.nextport: - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb .portloop -.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 -; 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. -; Otherwise, call ehci_new_port. -; This should be done after step 3. - xor ecx, ecx - cmp [esi+usb_controller.NewConnected], ecx - jz .skip_newconnected -.portloop2: - bt [esi+usb_controller.NewConnected], ecx - jnc .noconnect - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ConnectedTime+ecx*4] - sub eax, USB_CONNECT_DELAY - jge .connected - neg eax - cmp [esp], eax - jb .nextport2 - mov [esp], eax - jmp .nextport2 -.connected: - btr [esi+usb_controller.NewConnected], ecx - call ehci_new_port - jmp .portloop2 -.noconnect: -.nextport2: - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb .portloop2 -.skip_newconnected: -; 6. Process wait lists. -; 6a. Periodic endpoints. -; If a request is pending >8 microframes, satisfy it. -; If a request is pending <=8 microframes, schedule next wakeup in 0.01s. - mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] - cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic] - jz .noperiodic - mov edx, [edi+EhciFrameIndexReg] - sub edx, [esi+usb_controller.StartWaitFrame] - and edx, 0x3FFF - cmp edx, 8 - jbe @f - mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax - jmp .noperiodic -@@: - pop eax - push 1 ; wakeup in 0.01 sec for next test -.noperiodic: -; 6b. Asynchronous endpoints. -; Satisfy a request when InterruptOnAsyncAdvance fired. - test byte [esp+4], 20h - jz @f - dbgstr 'async advance int' - mov eax, [esi+usb_controller.WaitPipeRequestAsync] - mov [esi+usb_controller.ReadyPipeHeadAsync], eax -@@: -; Some hardware in some (rarely) conditions set the status bit, -; but just does not generate the corresponding interrupt. -; Force checking the status here. - mov eax, [esi+usb_controller.WaitPipeRequestAsync] - cmp [esi+usb_controller.ReadyPipeHeadAsync], eax - jz .noasync - spin_lock_irq [esi+usb_controller.WaitSpinlock] - mov edx, [edi+EhciStatusReg] - test dl, 20h - jz @f - mov dword [edi+EhciStatusReg], 20h - and dword [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], not 20h - dbgstr 'warning: async advance int missed' - mov [esi+usb_controller.ReadyPipeHeadAsync], eax - jmp .async_unlock -@@: - cmp dword [esp], 100 - jb .async_unlock - mov dword [esp], 100 -.async_unlock: - spin_unlock_irq [esi+usb_controller.WaitSpinlock] -.noasync: -; 7. Finalize transfers processed by hardware. -; It is better to perform this step after step 4 (disconnect events), -; although not strictly obligatory. This way, an active transfer aborted -; due to disconnect would be handled with more specific USB_STATUS_CLOSED, -; not USB_STATUS_NORESPONSE. - test byte [esp+4], 3 - jz @f - call ehci_process_updated_schedule -@@: -; 8. Test whether reset signalling has been started and should be stopped now. -; This must be done after step 7, because completion of some transfer could -; result in resetting a new port. -.test_reset: -; 8a. Test whether reset signalling is active. - cmp [esi+usb_controller.ResettingStatus], 1 - jnz .no_reset_in_progress -; 8b. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ResetTime] - sub eax, USB_RESET_TIME - jge .reset_done -; 8c. Not yet, but initiate wakeup in -eax ticks and exit this step. - neg eax - cmp [esp], eax - jb .skip_reset - mov [esp], eax - jmp .skip_reset -.reset_done: -; 8d. Yep, call the worker function and proceed to 8e. - call ehci_port_reset_done -.no_reset_in_progress: -; 8e. Test whether reset process is done, either successful or failed. - cmp [esi+usb_controller.ResettingStatus], 0 - jz .skip_reset -; 8f. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ResetTime] - sub eax, USB_RESET_RECOVERY_TIME - jge .reset_recovery_done -; 8g. Not yet, but initiate wakeup in -eax ticks and exit this step. - neg eax - cmp [esp], eax - jb .skip_reset - mov [esp], eax - jmp .skip_reset -.reset_recovery_done: -; 8h. Yep, call the worker function. This could initiate another reset, -; so return to the beginning of this step. - call ehci_port_init - jmp .test_reset -.skip_reset: -; 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 -; 9b. If it reports that an asynchronous endpoint should be removed, -; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s -; (sometimes it just does not fire). - test al, 1 shl CONTROL_PIPE - jz @f - mov edx, [esi+usb_controller.WaitPipeListAsync] - mov [esi+usb_controller.WaitPipeRequestAsync], edx - or dword [edi+EhciCommandReg], 1 shl 6 - dbgstr 'async advance doorbell' - cmp dword [esp], 100 - jb @f - mov dword [esp], 100 -@@: -; 9c. If it reports that a periodic endpoint should be removed, -; save the current frame and schedule wakeup in 0.01 sec. - test al, 1 shl INTERRUPT_PIPE - jz @f - mov eax, [esi+usb_controller.WaitPipeListPeriodic] - mov [esi+usb_controller.WaitPipeRequestPeriodic], eax - mov edx, [edi+EhciFrameIndexReg] - mov [esi+usb_controller.StartWaitFrame], edx - mov dword [esp], 1 ; wakeup in 0.01 sec for next test -@@: -; 10. Pop the return value, restore the stack after step 1 and return. - pop eax - pop ecx - pop edi ebx ; restore used registers to be stdcall - ret -endp - -; This procedure is called in the USB thread from ehci_process_deferred -; when EHCI IRQ handler has signalled that new IOC-packet was processed. -; It scans all lists for completed packets and calls ehci_process_finalized_td -; for those packets. -proc ehci_process_updated_schedule -; Important note: we cannot hold the list lock during callbacks, -; because callbacks sometimes open and/or close pipes and thus acquire/release -; the corresponding lock itself. -; Fortunately, pipes can be finally freed only by another step of -; ehci_process_deferred, so all pipes existing at the start of this function -; will be valid while this function is running. Some pipes can be removed -; from the corresponding list, some pipes can be inserted; insert/remove -; functions guarantee that traversing one list yields all pipes that were in -; that list at the beginning of the traversing (possibly with some new pipes, -; possibly without some new pipes, that doesn't matter). - push edi -; 1. Process all Periodic lists. - lea edi, [esi+ehci_controller.IntEDs-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] - lea ebx, [esi+ehci_controller.IntEDs+63*sizeof.ehci_static_ep-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] -@@: - call ehci_process_updated_list - cmp edi, ebx - jnz @b -; 2. Process the Control list. - add edi, ehci_controller.ControlDelta - call ehci_process_updated_list -; 3. Process the Bulk list. - call ehci_process_updated_list -; 4. Return. - pop edi - ret -endp - -; This procedure is called from ehci_process_updated_schedule, see comments there. -; It processes one list, esi -> usb_controller, edi -> usb_static_ep, -; and advances edi to next head. -proc ehci_process_updated_list - push ebx -; 1. Perform the external loop over all pipes. - mov ebx, [edi+usb_static_ep.NextVirt] -.loop: - cmp ebx, edi - jz .done -; store pointer to the next pipe in the stack - push [ebx+usb_static_ep.NextVirt] -; 2. For every pipe, perform the internal loop over all descriptors. -; All descriptors are organized in the queue; we process items from the start -; 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 - mov ebx, [ebx+usb_pipe.LastTD] - push ebx - mov ebx, [ebx+usb_gtd.NextVirt] -.tdloop: -; 3. For every descriptor, test active flag and check for end-of-queue; -; if either of conditions holds, exit from the internal loop. - cmp ebx, [esp] - jz .tddone - cmp byte [ebx+ehci_gtd.Token-sizeof.ehci_gtd], 0 - js .tddone -; Release the queue lock while processing one descriptor: -; callback function could (and often would) schedule another transfer. - push ecx - call mutex_unlock - call ehci_process_updated_td - pop ecx - call mutex_lock - jmp .tdloop -.tddone: - call mutex_unlock - pop ebx -; End of internal loop, restore pointer to the next pipe -; and continue the external loop. - pop ebx - jmp .loop -.done: - pop ebx - add edi, sizeof.ehci_static_ep - ret -endp - -; This procedure is called from ehci_process_updated_list, which is itself -; called from ehci_process_updated_schedule, see comments there. -; It processes one completed descriptor. -; in: ebx -> usb_gtd, out: ebx -> next usb_gtd. -proc ehci_process_updated_td -; mov eax, [ebx+usb_gtd.Pipe] -; cmp [eax+usb_pipe.Type], INTERRUPT_PIPE -; jnz @f -; DEBUGF 1,'K : finalized TD for pipe %x:\n',eax -; lea eax, [ebx-sizeof.ehci_gtd] -; DEBUGF 1,'K : %x %x %x %x\n',[eax],[eax+4],[eax+8],[eax+12] -; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28] -;@@: -; 1. Remove this descriptor from the list of descriptors for this pipe. - call usb_unlink_td -; 2. Calculate actual number of bytes transferred. - mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] - lea edx, [eax+eax] - shr edx, 17 - sub edx, [ebx+usb_gtd.Length] - neg edx -; 3. Check whether we need some special processing beyond notifying the driver. -; Transfer errors require special processing. -; Short packets require special processing if -; a) this is not the last descriptor for transfer stage -; (in this case we need to process subsequent descriptors for the stage too) -; or b) the caller considers short transfers to be an error. -; ehci_alloc_transfer sets bit 0 of ehci_gtd.Flags to 0 if short packet -; in this descriptor requires special processing and to 1 otherwise. -; If special processing is not needed, advance to 4 with ecx = 0. -; Otherwise, go to 6. - xor ecx, ecx - test al, 40h - jnz .error - test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 - jnz .notify - cmp edx, [ebx+usb_gtd.Length] - jnz .special -.notify: -; 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: -; 5. Free the current descriptor and return the next one. - push [ebx+usb_gtd.NextVirt] - stdcall ehci_free_td, ebx - pop ebx - ret -.error: - push ebx - sub ebx, sizeof.ehci_gtd - DEBUGF 1,'K : TD failed:\n' - DEBUGF 1,'K : %x %x %x %x\n',[ebx],[ebx+4],[ebx+8],[ebx+12] - DEBUGF 1,'K : %x %x %x %x\n',[ebx+16],[ebx+20],[ebx+24],[ebx+28] - pop ebx - DEBUGF 1,'K : pipe now:\n' - mov ecx, [ebx+usb_gtd.Pipe] - sub ecx, sizeof.ehci_pipe - DEBUGF 1,'K : %x %x %x %x\n',[ecx],[ecx+4],[ecx+8],[ecx+12] - DEBUGF 1,'K : %x %x %x %x\n',[ecx+16],[ecx+20],[ecx+24],[ecx+28] - DEBUGF 1,'K : %x %x %x %x\n',[ecx+32],[ecx+36],[ecx+40],[ecx+44] -.special: -; 6. Special processing is needed. -; 6a. Save the status and length. - push edx - push eax -; 6b. Traverse the list of descriptors looking for the final descriptor -; for this transfer. Free and unlink non-final descriptors. -; Final descriptor will be freed in step 5. -.look_final: - call usb_is_final_packet - jnc .found_final - push [ebx+usb_gtd.NextVirt] - stdcall ehci_free_td, ebx - pop ebx - call usb_unlink_td - jmp .look_final -.found_final: -; 6c. Restore the status saved in 6a and transform it to the error code. -; Notes: -; * any USB transaction error results in Halted bit; if it is not set, -; but we are here, it must be due to short packet; -; * babble is considered a fatal USB transaction error, -; other errors just lead to retrying the transaction; -; if babble is detected, return the corresponding error; -; * if several non-fatal errors have occured during transaction retries, -; all corresponding bits are set. In this case, return some error code, -; the order is quite arbitrary. - pop eax ; status - movi ecx, USB_STATUS_UNDERRUN - test al, 40h ; not Halted? - jz .know_error - mov cl, USB_STATUS_OVERRUN - test al, 10h ; Babble detected? - jnz .know_error - mov cl, USB_STATUS_BUFOVERRUN - test al, 20h ; Data Buffer error? - jnz .know_error - mov cl, USB_STATUS_NORESPONSE - test al, 8 ; Transaction Error? - jnz .know_error - mov cl, USB_STATUS_STALL -.know_error: -; 6d. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets, -; it is not an error; in this case, go to 4 with ecx = 0. - cmp ecx, USB_STATUS_UNDERRUN - jnz @f - test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 - jz @f - xor ecx, ecx - pop edx ; length - jmp .notify -@@: -; 6e. Abort the entire transfer. -; There are two cases: either there is only one transfer stage -; (everything except control transfers), then ebx points to the last TD and -; all previous TD were unlinked and dismissed (if possible), -; or there are several stages (a control transfer) and ebx points to the last -; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, -; because Setup stage can not produce short packets); for Data stage, we need -; to unlink and free (if possible) one more TD and advance ebx to the next one. - cmp [ebx+usb_gtd.Callback], 0 - jnz .normal - push ecx - push [ebx+usb_gtd.NextVirt] - stdcall ehci_free_td, ebx - pop ebx - call usb_unlink_td - pop ecx -.normal: -; 6f. For bulk/interrupt transfers we have no choice but halt the queue, -; the driver should intercede (through some API which is not written yet). -; Control pipes normally recover at the next SETUP transaction (first stage -; of any control transfer), so we hope on the best and just advance the queue -; to the next transfer. (According to the standard, "A control pipe may also -; support functional stall as well, but this is not recommended."). - mov edx, [ebx+usb_gtd.Pipe] - mov eax, [ebx+ehci_gtd.NextTD-sizeof.ehci_gtd] - or al, 1 - mov [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax - mov [edx+ehci_pipe.Overlay.AlternateNextTD-sizeof.ehci_pipe], eax - cmp [edx+usb_pipe.Type], CONTROL_PIPE - jz .control -; Bulk/interrupt transfer; halt the queue. - mov [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 40h - pop edx - jmp .notify -; Control transfer. -.control: - and [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 0 - dec [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe] - pop edx - jmp .notify -endp - -; This procedure unlinks the pipe from the corresponding pipe list. -; esi -> usb_controller, ebx -> usb_pipe -proc ehci_unlink_pipe - cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE - jnz @f - test word [ebx+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh - jnz .interrupt_fs - call ehci_hs_interrupt_list_unlink - jmp .interrupt_common -.interrupt_fs: - call ehci_fs_interrupt_list_unlink -.interrupt_common: -@@: - mov edx, [ebx+usb_pipe.NextVirt] - mov eax, [ebx+usb_pipe.PrevVirt] - mov [edx+usb_pipe.PrevVirt], eax - mov [eax+usb_pipe.NextVirt], edx - mov edx, esi - sub edx, eax - cmp edx, sizeof.ehci_controller - mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe] - jb .prev_is_static - mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx - ret -.prev_is_static: - mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx - ret -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 - test eax, eax - jz @f - add eax, sizeof.ehci_gtd -@@: - pop ebx - ret -endp - -; This procedure is called from several places from main USB code and -; frees all additional data associated with the transfer descriptor. -; EHCI has no additional data, so just free ehci_gtd structure. -proc ehci_free_td - sub dword [esp+4], sizeof.ehci_gtd - jmp usb_free_common -endp diff --git a/kernel/branches/Kolibri-acpi/bus/usb/hccommon.inc b/kernel/branches/Kolibri-acpi/bus/usb/hccommon.inc index 10b287002c..4a066859a3 100644 --- a/kernel/branches/Kolibri-acpi/bus/usb/hccommon.inc +++ b/kernel/branches/Kolibri-acpi/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/branches/Kolibri-acpi/bus/usb/hub.inc b/kernel/branches/Kolibri-acpi/bus/usb/hub.inc index 840b556acd..6048076841 100644 --- a/kernel/branches/Kolibri-acpi/bus/usb/hub.inc +++ b/kernel/branches/Kolibri-acpi/bus/usb/hub.inc @@ -219,30 +219,48 @@ virtual at esp .config dd ? ; pointer to usb_config_descr .interface dd ? ; pointer to usb_interface_descr end virtual +; 1. Check that the maximal nesting is not exceeded: +; 5 non-root hubs is the maximum according to the spec. + mov ebx, [.pipe] + push 5 + mov eax, ebx +.count_parents: + mov eax, [eax+usb_pipe.DeviceData] + mov eax, [eax+usb_device_data.Hub] + test eax, eax + jz .depth_ok + mov eax, [eax+usb_hub.ConfigPipe] + dec dword [esp] + jnz .count_parents + pop eax + dbgstr 'Hub chain is too long' + jmp .return0 +.depth_ok: + pop eax ; Hubs use one IN interrupt endpoint for polling the device -; 1. Locate the descriptor of the interrupt endpoint. +; 2. Locate the descriptor of the interrupt endpoint. ; Loop over all descriptors owned by this interface. .lookep: -; 1a. Skip the current descriptor. +; 2a. Skip the current descriptor. movzx eax, [edx+usb_descr.bLength] add edx, eax sub ecx, eax jb .errorep -; 1b. Length of data left must be at least sizeof.usb_endpoint_descr. +; 2b. Length of data left must be at least sizeof.usb_endpoint_descr. cmp ecx, sizeof.usb_endpoint_descr jb .errorep -; 1c. If we have found another interface descriptor but not found our endpoint, +; 2c. If we have found another interface descriptor but not found our endpoint, ; this is an error: all subsequent descriptors belong to that interface ; (or further interfaces). cmp [edx+usb_endpoint_descr.bDescriptorType], USB_INTERFACE_DESCR jz .errorep -; 1d. Ignore all interface-related descriptors except endpoint descriptor. +; 2d. Ignore all interface-related descriptors except endpoint descriptor. cmp [edx+usb_endpoint_descr.bDescriptorType], USB_ENDPOINT_DESCR jnz .lookep -; 1e. Length of endpoint descriptor must be at least sizeof.usb_endpoint_descr. +; 2e. Length of endpoint descriptor must be at least sizeof.usb_endpoint_descr. cmp [edx+usb_endpoint_descr.bLength], sizeof.usb_endpoint_descr jb .errorep -; 1f. Ignore all endpoints except for INTERRUPT IN. +; 2f. Ignore all endpoints except for INTERRUPT IN. cmp [edx+usb_endpoint_descr.bEndpointAddress], 0 jge .lookep mov al, [edx+usb_endpoint_descr.bmAttributes] @@ -251,23 +269,22 @@ end virtual jnz .lookep ; We have located the descriptor for INTERRUPT IN endpoint, ; the pointer is in edx. -; 2. Allocate memory for the hub descriptor. +; 3. Allocate memory for the hub descriptor. ; Maximum length (assuming 255 downstream ports) is 40 bytes. ; Allocate 4 extra bytes to keep wMaxPacketSize. -; 2a. Save registers. +; 3a. Save registers. push edx -; 2b. Call the allocator. +; 3b. Call the allocator. movi eax, 44 call malloc -; 2c. Restore registers. +; 3c. Restore registers. pop ecx -; 2d. If failed, say something to the debug board and return error. +; 3d. If failed, say something to the debug board and return error. test eax, eax jz .nomemory -; 2e. Store the pointer in esi. xchg eax,r32 is one byte shorter than mov. +; 3e. Store the pointer in esi. xchg eax,r32 is one byte shorter than mov. xchg esi, eax -; 3. Open a pipe for the status endpoint with descriptor found in step 1. - mov ebx, [.pipe] +; 4. Open a pipe for the status endpoint with descriptor found in step 1. movzx eax, [ecx+usb_endpoint_descr.bEndpointAddress] movzx edx, [ecx+usb_endpoint_descr.bInterval] movzx ecx, [ecx+usb_endpoint_descr.wMaxPacketSize] @@ -276,11 +293,11 @@ end virtual push ecx stdcall usb_open_pipe, ebx, eax, ecx, INTERRUPT_PIPE, edx pop ecx -; If failed, free the memory allocated in step 2, +; If failed, free the memory allocated in step 3, ; say something to the debug board and return error. test eax, eax jz .free -; 4. Send control query for the hub descriptor, +; 5. Send control query for the hub descriptor, ; pass status pipe as a callback parameter, ; allow short packets. and ecx, (1 shl 11) - 1 @@ -291,7 +308,7 @@ end virtual (USB_HUB_DESCRIPTOR shl 24) mov dword [esi+4], 40 shl 16 stdcall usb_control_async, ebx, esi, esi, 40, usb_hub_got_config, eax, 1 -; 5. If failed, free the memory allocated in step 2, +; 6. If failed, free the memory allocated in step 3, ; say something to the debug board and return error. test eax, eax jz .free @@ -1245,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/branches/Kolibri-acpi/bus/usb/init.inc b/kernel/branches/Kolibri-acpi/bus/usb/init.inc index 90ea90f691..7587bb881b 100644 --- a/kernel/branches/Kolibri-acpi/bus/usb/init.inc +++ b/kernel/branches/Kolibri-acpi/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.list.next] 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/branches/Kolibri-acpi/bus/usb/memory.inc b/kernel/branches/Kolibri-acpi/bus/usb/memory.inc index f8cdcae3c6..8a24b656b3 100644 --- a/kernel/branches/Kolibri-acpi/bus/usb/memory.inc +++ b/kernel/branches/Kolibri-acpi/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/branches/Kolibri-acpi/bus/usb/ohci.inc b/kernel/branches/Kolibri-acpi/bus/usb/ohci.inc deleted file mode 100644 index 254bd39585..0000000000 --- a/kernel/branches/Kolibri-acpi/bus/usb/ohci.inc +++ /dev/null @@ -1,1599 +0,0 @@ -; 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. - -; ============================================================================= -; ================================= Constants ================================= -; ============================================================================= -; OHCI register declarations -; All of the registers should be read and written as Dwords. -; Partition 1. Control and Status registers. -OhciRevisionReg = 0 -OhciControlReg = 4 -OhciCommandStatusReg = 8 -OhciInterruptStatusReg = 0Ch -OhciInterruptEnableReg = 10h -OhciInterruptDisableReg = 14h -; Partition 2. Memory Pointer registers. -OhciHCCAReg = 18h -OhciPeriodCurrentEDReg = 1Ch -OhciControlHeadEDReg = 20h -OhciControlCurrentEDReg = 24h -OhciBulkHeadEDReg = 28h -OhciBulkCurrentEDReg = 2Ch -OhciDoneHeadReg = 30h -; Partition 3. Frame Counter registers. -OhciFmIntervalReg = 34h -OhciFmRemainingReg = 38h -OhciFmNumberReg = 3Ch -OhciPeriodicStartReg = 40h -OhciLSThresholdReg = 44h -; Partition 4. Root Hub registers. -OhciRhDescriptorAReg = 48h -OhciRhDescriptorBReg = 4Ch -OhciRhStatusReg = 50h -OhciRhPortStatusReg = 54h - -; ============================================================================= -; ================================ Structures ================================= -; ============================================================================= - -; OHCI-specific part of a pipe descriptor. -; * This structure corresponds to the Endpoint Descriptor aka ED from the OHCI -; specification. -; * The hardware requires 16-bytes alignment of the hardware part. -; Since the allocator (usb_allocate_common) allocates memory sequentially -; from page start (aligned on 0x1000 bytes), block size for the allocator -; must be divisible by 16; usb1_allocate_endpoint ensures this. -struct ohci_pipe -; All addresses are physical. -Flags dd ? -; 1. Lower 7 bits (bits 0-6) are FunctionAddress. This is the USB address of -; the function containing the endpoint that this ED controls. -; 2. Next 4 bits (bits 7-10) are EndpointNumber. This is the USB address of -; the endpoint within the function. -; 3. Next 2 bits (bits 11-12) are Direction. This 2-bit field indicates the -; direction of data flow: 1 = IN, 2 = OUT. If neither IN nor OUT is -; specified, then the direction is determined from the PID field of the TD. -; For CONTROL endpoints, the transfer direction is different -; for different transfers, so the value of this field is 0 -; (3 would have the same effect) and the actual direction -; of one transfer is encoded in the Transfer Descriptor. -; 4. Next bit (bit 13) is Speed bit. It indicates the speed of the endpoint: -; full-speed (S = 0) or low-speed (S = 1). -; 5. Next bit (bit 14) is sKip bit. When this bit is set, the hardware -; continues on to the next ED on the list without attempting access -; to the TD queue or issuing any USB token for the endpoint. -; Always cleared. -; 6. Next bit (bit 15) is Format bit. It must be 0 for Control, Bulk and -; Interrupt endpoints and 1 for Isochronous endpoints. -; 7. Next 11 bits (bits 16-26) are MaximumPacketSize. This field indicates -; the maximum number of bytes that can be sent to or received from the -; endpoint in a single data packet. -TailP dd ? -; Physical address of the tail descriptor in the TD queue. -; The descriptor itself is not in the queue. See also HeadP. -HeadP dd ? -; 1. First bit (bit 0) is Halted bit. This bit is set by the hardware to -; indicate that processing of the TD queue on the endpoint is halted. -; 2. Second bit (bit 1) is toggleCarry bit. Whenever a TD is retired, this -; bit is written to contain the last data toggle value from the retired TD. -; 3. Next two bits (bits 2-3) are reserved and always zero. -; 4. With masked 4 lower bits, this is HeadP itself: physical address of the -; head descriptor in the TD queue, that is, next TD to be processed for this -; endpoint. Note that a TD must be 16-bytes aligned. -; Empty queue is characterized by the condition HeadP == TailP. -NextED dd ? -; If nonzero, then this entry is a physical address of the next ED to be -; processed. See also the description before NextVirt field of the usb_pipe -; structure. Additionally to that description, the following is specific for -; the OHCI controller: -; * n=5, N=32, there are 32 "leaf" periodic lists. -; * The 1ms periodic list also serves Isochronous endpoints, which should be -; in the end of the list. -; * There is no "next" list for Bulk and Control lists, they are processed -; separately from others. -; * There is no "next" list for Periodic list for 1ms interval. -ends - -; This structure describes the static head of every list of pipes. -; The hardware requires 16-bytes alignment of this structure. -; All instances of this structure are located sequentially in uhci_controller, -; uhci_controller is page-aligned, so it is sufficient to make this structure -; 16-bytes aligned and verify that the first instance is 16-bytes aligned -; inside uhci_controller. -struct ohci_static_ep -Flags dd ? -; Same as ohci_pipe.Flags. -; sKip bit is set, so the hardware ignores other fields except NextED. - dd ? -; Corresponds to ohci_pipe.TailP. Not used. -NextList dd ? -; Virtual address of the next list. -NextED dd ? -; Same as ohci_pipe.NextED. -SoftwarePart rd sizeof.usb_static_ep/4 -; Software part, common for all controllers. - dd ? -; Padding for 16-bytes alignment. -ends - -if sizeof.ohci_static_ep mod 16 -.err ohci_static_ep must be 16-bytes aligned -end if - -; OHCI-specific part of controller data. -; * The structure describes the memory area used for controller data, -; additionally to the registers of the controller. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 256 bytes and corresponds to -; the HCCA from OHCI specification. -; * The hardware requires 256-bytes alignment of the hardware part, so -; the entire descriptor must be 256-bytes aligned. -; This structure is allocated with kernel_alloc (see usb_init_controller), -; this gives page-aligned data. -; * The controller is described by both ohci_controller and usb_controller -; structures, for each controller there is one ohci_controller and one -; usb_controller structure. These structures are located sequentially -; in the memory: beginning from some page start, there is ohci_controller -; structure - this enforces hardware alignment requirements - and then -; usb_controller structure. -; * The code keeps pointer to usb_controller structure. The ohci_controller -; structure is addressed as [ptr + ohci_controller.field - sizeof.ohci_controller]. -struct ohci_controller -; ------------------------------ hardware fields ------------------------------ -InterruptTable rd 32 -; Pointers to interrupt EDs. The hardware starts processing of periodic lists -; within the frame N from the ED pointed to by [InterruptTable+(N and 31)*4]. -; See also the description of periodic lists inside ohci_pipe structure. -FrameNumber dw ? -; The current frame number. This field is written by hardware only. -; This field is read by ohci_process_deferred and ohci_irq to -; communicate when control/bulk processing needs to be temporarily -; stopped/restarted. - dw ? -; Padding. Written as zero at every update of FrameNumber. -DoneHead dd ? -; Physical pointer to the start of Done Queue. -; When the hardware updates this field, it sets bit 0 to one if there is -; unmasked interrupt pending. - rb 120 -; Reserved for the hardware. -; ------------------------------ software fields ------------------------------ -IntEDs ohci_static_ep - rb 62 * sizeof.ohci_static_ep -; Heads of 63 Periodic lists, see the description in usb_pipe. -ControlED ohci_static_ep -; Head of Control list, see the description in usb_pipe. -BulkED ohci_static_ep -; Head of Bulk list, see the description in usb_pipe. -MMIOBase dd ? -; Virtual address of memory-mapped area with OHCI registers OhciXxxReg. -PoweredUp db ? -; 1 in normal work, 0 during early phases of the initialization. -; This field is initialized to zero during memory allocation -; (see usb_init_controller), set to one by ohci_init when ports of the root hub -; are powered up, so connect/disconnect events can be handled. - rb 3 ; alignment -DoneList dd ? -; List of descriptors which were processed by the controller and now need -; to be finalized. -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. -ends - -if ohci_controller.IntEDs mod 16 -.err Static endpoint descriptors must be 16-bytes aligned inside ohci_controller -end if - -; OHCI general transfer descriptor. -; * The structure describes transfers to be performed on Control, Bulk or -; Interrupt endpoints. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 16 bytes and corresponds to -; the General Transfer Descriptor aka general TD from OHCI specification. -; * The hardware requires 16-bytes alignment of the hardware part, so -; the entire descriptor must be 16-bytes aligned. Since the allocator -; (usb_allocate_common) allocates memory sequentially from page start -; (aligned on 0x1000 bytes), block size for the allocator must be -; divisible by 16; usb1_allocate_generic_td ensures this. -struct ohci_gtd -; ------------------------------ hardware fields ------------------------------ -; All addresses in this part are physical. -Flags dd ? -; 1. Lower 18 bits (bits 0-17) are ignored and not modified by the hardware. -; 2. Next bit (bit 18) is bufferRounding bit. If this bit is 0, then the last -; data packet must exactly fill the defined data buffer. If this bit is 1, -; then the last data packet may be smaller than the defined buffer without -; causing an error condition on the TD. -; 3. Next 2 bits (bits 19-20) are Direction field. This field indicates the -; direction of data flow. If the Direction field in the ED is OUT or IN, -; this field is ignored and the direction from the ED is used instead. -; Otherwise, 0 = SETUP, 1 = OUT, 2 = IN, 3 is reserved. -; 4. Next 3 bits (bits 21-23) are DelayInterrupt field. This field contains -; the interrupt delay count for this TD. When a TD is complete, the hardware -; may wait up to DelayInterrupt frames before generating an interrupt. -; If DelayInterrupt is 7 (maximum possible), then there is no interrupt -; associated with completion of this TD. -; 5. Next 2 bits (bits 24-25) are DataToggle field. This field is used to -; generate/compare the data PID value (DATA0 or DATA1). It is updated after -; each successful transmission/reception of a data packet. The bit 25 -; is 0 when the data toggle value is acquired from the toggleCarry field in -; the ED and 1 when the data toggle value is taken from the bit 24. -; 6. Next 2 bits (bits 26-27) are ErrorCount field. For each transmission -; error, this value is incremented. If ErrorCount is 2 and another error -; occurs, the TD is retired with error. When a transaction completes without -; error, ErrorCount is reset to 0. -; 7. Upper 4 bits (bits 28-31) are ConditionCode field. This field contains -; the status of the last attempted transaction, one of USB_STATUS_* values. -CurBufPtr dd ? -; Physical address of the next memory location that will be accessed for -; transfer to/from the endpoint. 0 means zero-length data packet or that all -; bytes have been transferred. -NextTD dd ? -; This field has different meanings depending on the status of the descriptor. -; When the descriptor is queued for processing, but not yet processed: -; Physical address of the next TD for the endpoint. -; When the descriptor is processed by hardware, but not yet by software: -; Physical address of the previous processed TD. -; When the descriptor is processed by the IRQ handler, but not yet completed: -; Virtual pointer to the next processed TD. -BufEnd dd ? -; Physical address of the last byte in the buffer for this TD. - dd ? ; padding to align with uhci_gtd -ends - -; OHCI isochronous transfer descriptor. -; * The structure describes transfers to be performed on Isochronous endpoints. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 32 bytes and corresponds to -; the Isochronous Transfer Descriptor aka isochronous TD from OHCI -; specification. -; * The hardware requires 32-bytes alignment of the hardware part, so -; the entire descriptor must be 32-bytes aligned. -; * The isochronous endpoints are not supported yet, so only hardware part is -; defined at the moment. -struct ohci_itd -StartingFrame dw ? -; This field contains the low order 16 bits of the frame number in which the -; first data packet of the Isochronous TD is to be sent. -Flags dw ? -; 1. Lower 5 bits (bits 0-4) are ignored and not modified by the hardware. -; 2. Next 3 bits (bits 5-7) are DelayInterrupt field. -; 3. Next 3 bits (bits 8-10) are FrameCount field. The TD describes -; FrameCount+1 data packets. -; 4. Next bit (bit 11) is ignored and not modified by the hardware. -; 5. Upper 4 bits (bits 12-15) are ConditionCode field. This field contains -; the completion code, one of USB_STATUS_* values, when the TD is moved to -; the Done Queue. -BufPage0 dd ? -; Lower 12 bits are ignored and not modified by the hardware. -; With masked 12 bits this field is the physical page containing all buffers. -NextTD dd ? -; Physical address of the next TD in the transfer queue. -BufEnd dd ? -; Physical address of the last byte in the buffer. -OffsetArray rw 8 -; Initialized by software, read by hardware: Offset for packet 0..7. -; Used to determine size and starting address of an isochronous data packet. -; Written by hardware, read by software: PacketStatusWord for packet 0..7. -; Contains completion code and, if applicable, size received for an isochronous -; data packet. -ends - -; Description of OHCI-specific data and functions for -; controller-independent code. -; Implements the structure usb_hardware_func from hccommon.inc for OHCI. -iglobal -align 4 -ohci_hardware_func: - dd 'OHCI' - dd sizeof.ohci_controller - dd ohci_init - dd ohci_process_deferred - dd ohci_set_device_address - dd ohci_get_device_address - 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_init_pipe - dd ohci_unlink_pipe - dd usb1_allocate_general_td - dd usb1_free_general_td - dd ohci_alloc_transfer - dd ohci_insert_transfer - dd ohci_new_device -endg - -; ============================================================================= -; =================================== Code ==================================== -; ============================================================================= - -; Controller-specific initialization function. -; Called from usb_init_controller. Initializes the hardware and -; OHCI-specific parts of software structures. -; eax = pointer to ohci_controller to be initialized -; [ebp-4] = pcidevice -proc ohci_init -; inherit some variables from the parent (usb_init_controller) -.devfn equ ebp - 4 -.bus equ ebp - 3 -; 1. Store pointer to ohci_controller for further use. - push eax - mov edi, eax -; 2. Initialize hardware fields of ohci_controller. -; Namely, InterruptTable needs to be initialized with -; physical addresses of heads of first 32 Periodic lists. -; Note that all static heads fit in one page, so one call -; to get_pg_addr is sufficient. -if (ohci_controller.IntEDs / 0x1000) <> (ohci_controller.BulkED / 0x1000) -.err assertion failed -end if -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 - add eax, ohci_controller.IntEDs - movi ecx, 32 - mov edx, ecx -@@: - stosd - add eax, sizeof.ohci_static_ep - loop @b -; 3. Initialize static heads ohci_controller.IntEDs, .ControlED, .BulkED. -; Use the loop over groups: first group consists of first 32 Periodic -; descriptors, next group consists of next 16 Periodic descriptors, -; ..., last group consists of the last Periodic descriptor. -; 3a. Prepare for the loop. -; make edi point to start of ohci_controller.IntEDs, -; other registers are already set. -; -128 fits in one byte, +128 does not fit. - sub edi, -128 -; 3b. Loop over groups. On every iteration: -; edx = size of group, edi = pointer to the current group, -; esi = pointer to the next group, eax = physical address of the next group. -.init_static_eds: -; 3c. Get the size of the next group. - shr edx, 1 -; 3d. Exit the loop if there is no next group. - jz .init_static_eds_done -; 3e. Initialize the first half of the current group. -; Advance edi to the second half. - push eax esi - call ohci_init_static_ep_group - pop esi eax -; 3f. Initialize the second half of the current group -; with the same values. -; Advance edi to the next group, esi/eax to the next of the next group. - call ohci_init_static_ep_group - jmp .init_static_eds -.init_static_eds_done: -; 3g. Initialize the head of the last Periodic list. - xor eax, eax - xor esi, esi - call ohci_init_static_endpoint -; 3i. Initialize the heads of Control and Bulk lists. - call ohci_init_static_endpoint - 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 - or al, 6 - xchg eax, ecx - call pci_write_reg -; 4b. Read memory base address. - mov ah, [.bus] - mov al, 2 - mov bl, 10h - call pci_read_reg - and al, not 0Fh -; 4c. Create mapping for physical memory. 256 bytes are sufficient. - stdcall map_io_mem, eax, 100h, PG_SW+PG_NOCACHE - test eax, eax - jz .fail - stosd ; fill ohci_controller.MMIOBase - xchg eax, edi -; now edi = MMIOBase -; 5. Reset the controller if needed. -; 5a. Check operational state. -; 0 = reset, 1 = resume, 2 = operational, 3 = suspended - mov eax, [edi+OhciControlReg] - and al, 3 shl 6 - cmp al, 2 shl 6 - jz .operational -; 5b. State is not operational, reset is needed. -.reset: -; 5c. Save FmInterval register. - pushd [edi+OhciFmIntervalReg] -; 5d. Issue software reset and wait up to 10ms, checking status every 1 ms. - movi ecx, 1 - movi edx, 10 - mov [edi+OhciCommandStatusReg], ecx -@@: - mov esi, ecx - call delay_ms - test [edi+OhciCommandStatusReg], ecx - jz .resetdone - dec edx - jnz @b - pop eax - dbgstr 'controller reset timeout' - jmp .fail_unmap -.resetdone: -; 5e. Restore FmInterval register. - pop eax - mov edx, eax - and edx, 3FFFh - jz .setfminterval - cmp dx, 2EDFh ; default value? - jnz @f ; assume that BIOS has configured the value -.setfminterval: - mov eax, 27792EDFh ; default value -@@: - mov [edi+OhciFmIntervalReg], eax -; 5f. Set PeriodicStart to 90% of FmInterval. - movzx eax, ax -; Two following lines are equivalent to eax = floor(eax * 0.9) -; for any 0 <= eax < 1C71C71Dh, which of course is far from maximum 0FFFFh. - mov edx, 0E6666667h - mul edx - mov [edi+OhciPeriodicStartReg], edx -.operational: -; 6. Setup controller registers. - pop esi ; restore pointer to ohci_controller saved in step 1 -; 6a. Physical address of HCCA. - mov eax, esi - call get_pg_addr - mov [edi+OhciHCCAReg], eax -; 6b. Transition to operational state and clear all Enable bits. - mov cl, 2 shl 6 - mov [edi+OhciControlReg], ecx -; 6c. Physical addresses of head of Control and Bulk lists. -if ohci_controller.BulkED >= 0x1000 -.err assertion failed -end if - add eax, ohci_controller.ControlED - mov [edi+OhciControlHeadEDReg], eax - add eax, ohci_controller.BulkED - ohci_controller.ControlED - mov [edi+OhciBulkHeadEDReg], eax -; 6d. Zero Head registers: there are no active Control and Bulk descriptors yet. - xor eax, eax -; mov [edi+OhciPeriodCurrentEDReg], eax - mov [edi+OhciControlCurrentEDReg], eax - mov [edi+OhciBulkCurrentEDReg], eax -; 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. - add esi, sizeof.ohci_controller - mov eax, [edi+OhciRhDescriptorAReg] - and eax, 0xF - mov [esi+usb_controller.NumPorts], eax -; 8. 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 -; al = IRQ - movzx eax, al - stdcall attach_int_handler, eax, ohci_irq, esi -; 10. 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. - mov dword [edi+OhciRhStatusReg], 10000h ; SetGlobalPower - xor ecx, ecx -@@: - mov dword [edi+OhciRhPortStatusReg+ecx*4], 1F0101h ; SetPortPower+ClearPortEnable+clear "changed" bits - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb @b -; 11b. 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 -@@: - pop esi -; 11c. 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; -; scan all ports manually and check for connected devices. - xor ecx, ecx -.port_loop: - test dword [edi+OhciRhPortStatusReg+ecx*4], 1 - jz .next_port -; There is a connected device; mark the port as 'connected' -; 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] - 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. - xchg eax, esi - ret -.fail_unmap: -; On error after step 4, release the virtual memory area. - stdcall free_kernel_space, 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, -; where that pointer is popped from the stack. - pop ecx -.nothing: - xor eax, eax - ret -endp - -; Helper procedure for step 3 of ohci_init. -; Initializes the static head of one list. -; eax = physical address of the "next" list, esi = pointer to the "next" list, -; edi = pointer to head to initialize. -; Advances edi to the next head, keeps eax/esi. -proc ohci_init_static_endpoint - mov byte [edi+ohci_static_ep.Flags+1], 1 shl (14 - 8) ; sKip this 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 - add edi, sizeof.ohci_static_ep - ohci_static_ep.SoftwarePart - ret -endp - -; Helper procedure for step 3 of ohci_init. -; Initializes one half of group of static heads. -; edx = size of the next group = half of size of the group, -; edi = pointer to the group, eax = physical address of the next group, -; esi = pointer to the next group. -; Advances eax, esi, edi to next group, keeps edx. -proc ohci_init_static_ep_group - push edx -@@: - call ohci_init_static_endpoint - add eax, sizeof.ohci_static_ep - add esi, sizeof.ohci_static_ep - dec edx - jnz @b - pop edx - ret -endp - -; Controller-specific pre-initialization function: take ownership from BIOS. -; Some BIOSes, although not all of them, provide legacy emulation -; for USB keyboard and/or mice as PS/2-devices. In this case, -; we must notify the BIOS that we don't need that emulation and know how to -; 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 - and al, not 0Fh -; 2. Create mapping for physical memory. 256 bytes are sufficient. - stdcall map_io_mem, eax, 100h, PG_SW+PG_NOCACHE - test eax, eax - jz .nothing -; 3. Some BIOSes enable controller interrupts as a result of giving -; controller away. At this point the system knows nothing about how to serve -; OHCI interrupts, so such an interrupt will send the system into an infinite -; loop handling the same IRQ again and again. Thus, we need to block OHCI -; interrupts. We can't do this at the controller level until step 5, -; because the controller is currently owned by BIOS, so we block all hardware -; interrupts on this processor until step 5. - pushf - cli -; 4. Take the ownership over the controller. -; 4a. Check whether BIOS handles this controller at all. - mov edx, 100h - test dword [eax+OhciControlReg], edx - jz .has_ownership -; 4b. Send "take ownership" command to the BIOS. -; (This should generate SMI, BIOS should release its ownership in SMI handler.) - mov dword [eax+OhciCommandStatusReg], 8 -; 4c. Wait for result no more than 50 ms, checking for status every 1 ms. - movi ecx, 50 -@@: - test dword [eax+OhciControlReg], edx - jz .has_ownership - push esi - movi esi, 1 - call delay_ms - pop esi - loop @b - dbgstr 'warning: taking OHCI ownership from BIOS timeout' -.has_ownership: -; 5. Disable all controller interrupts until the system will be ready to -; process them. - mov dword [eax+OhciInterruptDisableReg], 0C000007Fh -; 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 -.nothing: - ret -endp - -; IRQ handler for OHCI controllers. -ohci_irq.noint: -; Not our interrupt: restore registers and return zero. - xor eax, eax - pop edi esi ebx - ret - -proc ohci_irq - push ebx esi edi ; save used registers to be cdecl -virtual at esp - rd 3 ; saved registers - dd ? ; return address -.controller dd ? -end virtual -; 1. ebx will hold whether some deferred processing is needed, -; that cannot be done from the interrupt handler. Initialize to zero. - xor ebx, ebx -; 2. Get the mask of events which should be processed. - mov esi, [.controller] - mov edi, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller] - mov eax, [edi+OhciInterruptStatusReg] -; 3. Check whether that interrupt has been generated by our controller. -; (One IRQ can be shared by several devices.) - and eax, [edi+OhciInterruptEnableReg] - jz .noint -; 4. Get the physical pointer to the last processed descriptor. -; All processed descriptors form single-linked list from last to first -; with the help of NextTD field. The list is restarted every time when -; the controller writes to DoneHead, so grab the pointer now (before the next -; step) or it could be lost (the controller could write new value to DoneHead -; any time after WorkDone bit is cleared in OhciInterruptStatusReg). - mov ecx, [esi+ohci_controller.DoneHead-sizeof.ohci_controller] - and ecx, not 1 -; 5. Clear the events we know of. -; Note that this should be done before processing of events: -; new events could arise while we are processing those, this way we won't lose -; them (the controller would generate another interrupt -; after completion of this one). - mov [edi+OhciInterruptStatusReg], eax -; 6. Save the mask of events for further reference. - push eax -; 7. Handle 'transfer is done' events. -; 7a. Test whether there are such events. - test al, 2 - jz .skip_donehead -; There are some 'transfer is done' events, processed descriptors are linked -; through physical addresses in the reverse order. -; We can't do much in an interrupt handler, since callbacks could require -; waiting for locks and that can't be done in an interrupt handler. -; However, we can't also just defer all work to the USB thread, since -; it is possible that previous lists are not yet processed and it is hard -; to store unlimited number of list heads. Thus, we reverse the current list, -; append it to end of the previous list (if there is one) and defer other -; processing to the USB thread; this way there always is no more than one list -; (possibly joined from several controller-reported lists). -; The list traversal requires converting physical addresses to virtual pointers, -; so we may as well store pointers instead of physical addresses. -; 7b. Prepare for the reversing loop. - push ebx - xor ebx, ebx - test ecx, ecx - jz .tddone - call usb_td_to_virt - test eax, eax - jz .tddone - lea edx, [eax+ohci_gtd.NextTD] -; 7c. Reverse the list, converting physical to virtual. On every iteration: -; ecx = physical address of the current item -; eax = virtual pointer to the current item -; edx = virtual pointer to the last item.NextTD (first in the reverse list) -; ebx = virtual pointer to the next item (previous in the reverse list) -.tdloop: - mov ecx, [eax+ohci_gtd.NextTD] - mov [eax+ohci_gtd.NextTD], ebx - lea ebx, [eax+sizeof.ohci_gtd] - test ecx, ecx - jz .tddone - call usb_td_to_virt - test eax, eax - jnz .tdloop -.tddone: - mov ecx, ebx - pop ebx -; 7d. The list is reversed, -; ecx = pointer to the first item, edx = pointer to the last item.NextTD. -; If the list is empty (unusual case), step 7 is done. - test ecx, ecx - jz .skip_donehead -; 7e. Otherwise, append this list to the end of previous one. -; Note that in theory the interrupt handler and the USB thread -; could execute in parallel. -.append_restart: -; Atomically get DoneListEndPtr in eax and set it to edx. - mov eax, [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller] - lock cmpxchg [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], edx - jnz .append_restart -; Store pointer to the new list. -; Note: we cannot perform any operations with [DoneListEndPtr] -; until we switch DoneListEndPtr to a new descriptor: -; it is possible that after first line of .append_restart loop -; ohci_process_deferred obtains the control, finishes processing -; of the old list, sets DoneListEndPtr to address of DoneList, -; frees all old descriptors, so eax would point to invalid location. -; This way, .append_restart loop would detect that DoneListEndPtr -; has changed, so eax needs to be re-read. - mov [eax], ecx -; 7f. Notify the USB thread that there is new work. - inc ebx -.skip_donehead: -; 8. Handle start-of-frame events. -; 8a. Test whether there are such events. - test byte [esp], 4 - jz .skip_sof -; We enable SOF interrupt only when some pipes are waiting after changes. - spin_lock_irqsave [esi+usb_controller.WaitSpinlock] -; 8b. Make sure that there was at least one frame update -; since the request. If not, wait for the next SOF. - movzx eax, [esi+ohci_controller.FrameNumber-sizeof.ohci_controller] - cmp eax, [esi+usb_controller.StartWaitFrame] - jz .sof_unlock -; 8c. Copy WaitPipeRequest* to ReadyPipeHead*. - mov eax, [esi+usb_controller.WaitPipeRequestAsync] - mov [esi+usb_controller.ReadyPipeHeadAsync], eax - mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] - mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax -; 8d. It is possible that pipe change is due to removal and -; Control/BulkCurrentED registers still point to one of pipes to be removed. -; The code responsible for disconnect events has temporarily stopped -; Control/Bulk processing, so it is safe to clear Control/BulkCurrentED. -; After that, restart processing. - xor edx, edx - mov [edi+OhciControlCurrentEDReg], edx - mov [edi+OhciBulkCurrentEDReg], edx - mov dword [edi+OhciCommandStatusReg], 6 - or dword [edi+OhciControlReg], 30h -; 8e. Disable further interrupts on SOF. -; Note: OhciInterruptEnableReg/OhciInterruptDisableReg have unusual semantics. - mov dword [edi+OhciInterruptDisableReg], 4 -; Notify the USB thread that there is new work (with pipes from ReadyPipeHead*). - inc ebx -.sof_unlock: - spin_unlock_irqrestore [esi+usb_controller.RemoveSpinlock] -.skip_sof: -; Handle roothub events. -; 9. Test whether there are such events. - test byte [esp], 40h - jz .skip_roothub -; 10. Check the status of the roothub itself. -; 10a. Global overcurrent? - test dword [edi+OhciRhStatusReg], 2 - jz @f -; Note: this needs work. - dbgstr 'global overcurrent' -@@: -; 10b. Clear roothub events. - mov dword [edi+OhciRhStatusReg], 80020000h -; 11. Check the status of individual ports. -; Look for connect/disconnect and reset events. -; 11a. Prepare for the loop: start from port 0. - xor ecx, ecx -.portloop: -; 11b. Get the port status and changes of it. -; Accumulate change information. -; Look to "11.12.3 Port Change Information Processing" of the USB2 spec. - xor eax, eax -.accloop: - mov edx, [edi+OhciRhPortStatusReg+ecx*4] - xor ax, ax - or eax, edx - test edx, 1F0000h - jz .accdone - mov dword [edi+OhciRhPortStatusReg+ecx*4], 1F0000h - jmp .accloop -.accdone: -; debugging output, not needed for work -; test eax, 1F0000h -; jz @f -; DEBUGF 1,'K : ohci irq [%d] status of port %d is %x\n',[timer_ticks],ecx,eax -;@@: -; 11c. Ignore any events until all ports are powered up. -; They will be processed by ohci_init. - cmp [esi+ohci_controller.PoweredUp-sizeof.ohci_controller], 0 - jz .nextport -; Handle changing of connection status. - test eax, 10000h - jz .nocsc -; There was a connect or disconnect event at this port. -; 11d. Disconnect the old device on this port, if any. -; if the port was resetting, indicate fail and signal - cmp cl, [esi+usb_controller.ResettingPort] - jnz @f - mov [esi+usb_controller.ResettingStatus], -1 - inc ebx -@@: - lock bts [esi+usb_controller.NewDisconnected], ecx -; notify the USB thread that new work is waiting - inc ebx -; 11e. Change connected status. For the connection event, also -; store the connection time; any further processing is permitted only -; after USB_CONNECT_DELAY ticks. - test al, 1 - 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] - mov [esi+usb_controller.ConnectedTime+ecx*4], eax - lock bts [esi+usb_controller.NewConnected], ecx - jmp .nextport -.disconnect: - lock btr [esi+usb_controller.NewConnected], ecx - jmp .nextport -.nocsc: -; 11f. Process 'reset done' events. - test eax, 100000h - jz .nextport - test al, 10h - jnz .nextport - mov edx, [timer_ticks] - mov [esi+usb_controller.ResetTime], edx - mov [esi+usb_controller.ResettingStatus], 2 - inc ebx -.nextport: -; 11g. Continue the loop for the next port. - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb .portloop -.skip_roothub: -; 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 -; 14. Interrupt processed; return something non-zero. - mov al, 1 - pop edi esi ebx ; restore used registers to be stdcall - ret -endp - -; This procedure is called from usb_set_address_callback -; and stores USB device address in the ohci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe, cl = address -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 -endp - -; This procedure returns USB device address from the usb_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe -; out: eax = endpoint address -proc ohci_get_device_address - mov eax, [ebx+ohci_pipe.Flags-sizeof.ohci_pipe] - and eax, 7Fh - ret -endp - -; This procedure is called from usb_set_address_callback -; if the device does not accept SET_ADDRESS command and needs -; to be disabled at the port level. -; in: esi -> usb_controller, ecx = port -proc ohci_port_disable - mov edx, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller] - mov dword [edx+OhciRhPortStatusReg+ecx*4], 1 - ret -endp - -; This procedure is called from usb_get_descr8_callback when -; the packet size for zero endpoint becomes known and -; stores the packet size in ohci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size -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 -endp - -; This procedure is called from API usb_open_pipe and processes -; the controller-specific part of this API. See docs. -; 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 -proc ohci_init_pipe -virtual at ebp-12 -.speed db ? - rb 3 -.bandwidth dd ? -.target dd ? - rd 2 -.config_pipe dd ? -.endpoint dd ? -.maxpacket dd ? -.type dd ? -.interval dd ? -end virtual -; 1. Initialize the queue of transfer descriptors: empty. - sub eax, sizeof.ohci_gtd - call get_phys_addr - 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. - mov eax, [ecx+ohci_pipe.Flags-sizeof.ohci_pipe] - and eax, 0x207F ; keep Speed bit and FunctionAddress - mov edx, [.endpoint] - and edx, 15 - shl edx, 7 - or eax, edx - mov [edi+ohci_pipe.Flags-sizeof.ohci_pipe], eax - bt eax, 13 - setc [.speed] - mov eax, [.maxpacket] - mov word [edi+ohci_pipe.Flags+2-sizeof.ohci_pipe], ax - cmp [.type], CONTROL_PIPE - jz @f - test byte [.endpoint], 80h - setnz al - inc eax - shl al, 3 - or byte [edi+ohci_pipe.Flags+1-sizeof.ohci_pipe], al -@@: -; 3. Insert the new pipe to the corresponding list of endpoints. -; 3a. Use Control list for control pipes, Bulk list for bulk pipes. - lea edx, [esi+ohci_controller.ControlED.SoftwarePart-sizeof.ohci_controller] - cmp [.type], BULK_PIPE - jb .insert ; control pipe - lea edx, [esi+ohci_controller.BulkED.SoftwarePart-sizeof.ohci_controller] - jz .insert ; bulk pipe -.interrupt_pipe: -; 3b. For interrupt pipes, let the scheduler select the appropriate list -; based on the current bandwidth distribution and the requested bandwidth. -; This could fail if the requested bandwidth is not available; -; if so, return an error. - lea edx, [esi + ohci_controller.IntEDs - sizeof.ohci_controller] - lea eax, [esi + ohci_controller.IntEDs + 32*sizeof.ohci_static_ep - sizeof.ohci_controller] - movi ecx, 64 - call usb1_select_interrupt_list - test edx, edx - jz .return0 -; 3c. Insert endpoint at edi to the head of list in edx. -; Inserting to tail would work as well, -; but let's be consistent with other controllers. -.insert: - mov ecx, [edx+usb_pipe.NextVirt] - mov [edi+usb_pipe.NextVirt], ecx - mov [edi+usb_pipe.PrevVirt], edx - mov [ecx+usb_pipe.PrevVirt], edi - mov [edx+usb_pipe.NextVirt], edi - 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 - mov [edx+ohci_pipe.NextED-sizeof.ohci_pipe], eax -; 4. Return something non-zero. - ret -.return0: - xor eax, eax - ret -endp - -; This function is called from ohci_process_deferred when -; a new device was connected at least USB_CONNECT_DELAY ticks -; and therefore is ready to be configured. -; ecx = port, esi -> usb_controller -proc ohci_new_port -; test whether we are configuring another port -; if so, postpone configuring and return - bts [esi+usb_controller.PendingPorts], ecx - cmp [esi+usb_controller.ResettingPort], -1 - jnz .nothing - btr [esi+usb_controller.PendingPorts], ecx -; fall through to ohci_new_port.reset - -; This function is called from usb_test_pending_port. -; It starts reset signalling for the port. Note that in USB first stages -; of configuration can not be done for several ports in parallel. -.reset: -; reset port - and [esi+usb_controller.ResettingHub], 0 - mov [esi+usb_controller.ResettingPort], cl -; Note: setting status must be the last action: -; it is possible that the device has been disconnected -; after timeout of USB_CONNECT_DELAY but before call to ohci_new_port. -; In this case, ohci_irq would not set reset status to 'failed', -; because ohci_irq would not know that this port is to be reset. -; However, the hardware would generate another interrupt -; in a response to reset a disconnected port, and this time -; ohci_irq knows that it needs to generate 'reset failed' event -; (because ResettingPort is now filled). - push edi - mov edi, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller] - mov dword [edi+OhciRhPortStatusReg+ecx*4], 10h - pop edi -.nothing: - ret -endp - -; This procedure is called from the several places in main USB code -; and allocates required packets for the given transfer. -; ebx = 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 -proc ohci_alloc_transfer stdcall uses edi, \ - buffer:dword, size:dword, flags:dword, td:dword, direction:dword -locals -origTD dd ? -packetSize dd ? ; must be the last variable, see usb_init_transfer -endl -; 1. Save original value of td: -; it will be useful for rollback if something would fail. - mov eax, [td] - mov [origTD], eax -; One transfer descriptor can describe up to two pages. -; In the worst case (when the buffer is something*1000h+0FFFh) -; this corresponds to 1001h bytes. If the requested size is -; greater, we should split the transfer into several descriptors. -; Boundaries to split must be multiples of endpoint transfer size -; to avoid short packets except in the end of the transfer. - cmp [size], 1001h - jbe .lastpacket -; 2. While the remaining data cannot fit in one packet, -; allocate full-sized descriptors. -; 2a. Calculate size of one descriptor: must be a multiple of transfer size -; and must be not greater than 1001h. - movzx ecx, word [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe] - mov eax, 1001h - xor edx, edx - mov edi, eax - div ecx - sub edi, edx -; 2b. Allocate in loop. - mov [packetSize], edi -.fullpackets: - call ohci_alloc_packet - test eax, eax - jz .fail - mov [td], eax - add [buffer], edi - sub [size], edi - cmp [size], 1001h - ja .fullpackets -; 3. The remaining data can fit in one descriptor; -; allocate the last descriptor with size = size of remaining data. -.lastpacket: - mov eax, [size] - mov [packetSize], eax - call ohci_alloc_packet - test eax, eax - jz .fail -; 4. Enable an immediate interrupt on completion of the last packet. - and byte [ecx+ohci_gtd.Flags+2-sizeof.ohci_gtd], not (7 shl (21-16)) -; 5. If a short transfer is ok for a caller, set the corresponding bit in -; the last descriptor, but not in others. -; Note: even if the caller says that short transfers are ok, -; all packets except the last one are marked as 'must be complete': -; if one of them will be short, the software intervention is needed -; to skip remaining packets; ohci_process_finalized_td will handle this -; transparently to the caller. - test [flags], 1 - jz @f - or byte [ecx+ohci_gtd.Flags+2-sizeof.ohci_gtd], 1 shl (18-16) -@@: - ret -.fail: - mov edi, ohci_hardware_func - mov eax, [td] - stdcall usb_undo_tds, [origTD] - xor eax, eax - ret -endp - -; Helper procedure for ohci_alloc_transfer. -; Allocates and initializes one transfer descriptor. -; ebx = pipe, other parameters are passed through the stack; -; fills the current last descriptor and -; returns eax = next descriptor (not filled). -proc ohci_alloc_packet -; inherit some variables from the parent ohci_alloc_transfer -virtual at ebp-8 -.origTD dd ? -.packetSize dd ? - rd 2 -.buffer dd ? -.transferSize dd ? -.Flags dd ? -.td dd ? -.direction dd ? -end virtual -; 1. Allocate the next TD. - call usb1_allocate_general_td - test eax, eax - jz .nothing -; 2. Initialize controller-independent parts of both TDs. - push eax - call 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 - 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. - xor eax, eax - mov [ecx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd], eax - mov [ecx+ohci_gtd.BufEnd-sizeof.ohci_gtd], eax - cmp [.packetSize], eax - jz @f - mov eax, [.buffer] - call get_phys_addr - mov [ecx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd], eax - mov eax, [.buffer] - add eax, [.packetSize] - dec eax - call get_phys_addr - mov [ecx+ohci_gtd.BufEnd-sizeof.ohci_gtd], eax -@@: -; 6. Generate Flags field: -; - set bufferRounding (bit 18) to zero = disallow short transfers; -; for the last transfer in a row, ohci_alloc_transfer would set the real value; -; - set Direction (bits 19-20) to lower 2 bits of [.direction]; -; - set DelayInterrupt (bits 21-23) to 7 = do not generate interrupt; -; for the last transfer in a row, ohci_alloc_transfer would set the real value; -; - set DataToggle (bits 24-25) to next 2 bits of [.direction]; -; - set ConditionCode (bits 28-31) to 1111b as a indicator that there was no -; attempts to perform this transfer yet; -; - zero all other bits. - mov eax, [.direction] - mov edx, eax - and eax, 3 - shl eax, 19 - and edx, (3 shl 2) - shl edx, 24 - 2 - lea eax, [eax + edx + (7 shl 21) + (15 shl 28)] - mov [ecx+ohci_gtd.Flags-sizeof.ohci_gtd], eax -; 7. Restore the returned value saved in step 3. - pop eax -.nothing: - ret -endp - -; This procedure is called from the several places in main USB code -; and activates the transfer which was previously allocated by -; ohci_alloc_transfer. -; ecx -> last descriptor for the transfer, ebx -> usb_pipe -proc ohci_insert_transfer -; 1. Advance the queue of transfer descriptors. - mov eax, [ecx+ohci_gtd.NextTD-sizeof.ohci_gtd] - mov [ebx+ohci_pipe.TailP-sizeof.ohci_pipe], eax -; 2. For control and bulk pipes, notify the controller that -; there is new work in control/bulk queue respectively. -ohci_notify_new_work: - mov edx, [ebx+usb_pipe.Controller] - mov edx, [edx+ohci_controller.MMIOBase-sizeof.ohci_controller] - cmp [ebx+usb_pipe.Type], CONTROL_PIPE - jz .control - cmp [ebx+usb_pipe.Type], BULK_PIPE - jnz .nothing -.bulk: - mov dword [edx+OhciCommandStatusReg], 4 - jmp .nothing -.control: - mov dword [edx+OhciCommandStatusReg], 2 -.nothing: - ret -endp - -; This function is called from ohci_process_deferred when -; a new device has been reset and needs to be configured. -proc ohci_port_after_reset -; 1. Get the status. -; If reset has been failed (device disconnected during reset), -; continue to next device (if there is one). - xor eax, eax - xchg al, [esi+usb_controller.ResettingStatus] - test al, al - js 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] - mov eax, [edi+OhciRhPortStatusReg+ecx*4] - test al, 2 - jnz @f - DEBUGF 1,'K : USB port disabled after reset, status=%x\n',eax - jmp 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 - shr eax, 9 - call ohci_new_device - pop ecx -; 3. If something at the protocol layer has failed -; (no memory, no bus address), disable the port and stop the initialization. - test eax, eax - jnz .nothing -.disable_exit: - mov dword [edi+OhciRhPortStatusReg+ecx*4], 1 - jmp usb_test_pending_port -.nothing: - ret -endp - -; This procedure is called from uhci_port_init and from hub support code -; when a new device is connected and has been reset. -; It calls usb_new_device at the protocol layer with correct parameters. -; in: esi -> usb_controller, eax = speed; -; OHCI is USB1 device, so only low bit of eax (LowSpeed) is used. -proc ohci_new_device -; 1. Clear all bits of speed except bit 0. - and eax, 1 -; 2. Store the speed for the protocol layer. - mov [esi+usb_controller.ResettingSpeed], al -; 3. Create pseudo-pipe in the stack. -; See ohci_init_pipe: only .Controller and .Flags fields are used. - shl eax, 13 - push esi ; .Controller - mov ecx, esp - sub esp, 12 ; ignored fields - push eax ; .Flags -; 4. Notify the protocol layer. - call usb_new_device -; 5. Cleanup the stack after step 3 and return. - add esp, 20 - ret -endp - -; This procedure is called in the USB thread from usb_thread_proc, -; processes regular actions and those actions which can't be safely done -; from interrupt handler. -; Returns maximal time delta before the next call. -proc ohci_process_deferred - push ebx edi ; save used registers to be stdcall -; 1. Initialize the return value. - push -1 -; 2. Process disconnect events. - call 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. -; Otherwise, call ohci_new_port. - mov edi, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller] - xor ecx, ecx - cmp [esi+usb_controller.NewConnected], ecx - jz .skip_newconnected -.portloop: - bt [esi+usb_controller.NewConnected], ecx - jnc .noconnect - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ConnectedTime+ecx*4] - sub eax, USB_CONNECT_DELAY - jge .connected - neg eax - cmp [esp], eax - jb .nextport - mov [esp], eax - jmp .nextport -.connected: - lock btr [esi+usb_controller.NewConnected], ecx - jnc .nextport - call ohci_new_port -.noconnect: -.nextport: - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb .portloop -.skip_newconnected: -; 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] - sub eax, [esi+usb_controller.ResetTime] - sub eax, USB_RESET_RECOVERY_TIME - jge .reset_done - neg eax - cmp [esp], eax - jb .skip_roothub - mov [esp], eax - jmp .skip_roothub -.no_reset_recovery: - cmp [esi+usb_controller.ResettingStatus], 0 - jz .skip_roothub -.reset_done: - call ohci_port_after_reset -.skip_roothub: -; 5. Finalize transfers processed by hardware. -; It is better to perform this step after processing disconnect events, -; although not strictly obligatory. This way, an active transfer aborted -; due to disconnect would be handled with more specific USB_STATUS_CLOSED, -; not USB_STATUS_NORESPONSE. -; Loop over all items in DoneList, call ohci_process_finalized_td for each. - xor ebx, ebx - xchg ebx, [esi+ohci_controller.DoneList-sizeof.ohci_controller] -.tdloop: - test ebx, ebx - jz .tddone - call ohci_process_finalized_td - jmp .tdloop -.tddone: -; 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 -; 6b. If no new requests, skip the rest of this step. - test eax, eax - jz @f -; 6c. OHCI is not allowed to cache anything; we don't know what is -; processed right now, but we can be sure that the controller will not -; use any removed structure starting from the next frame. -; Schedule SOF event. - spin_lock_irq [esi+usb_controller.RemoveSpinlock] - mov eax, [esi+usb_controller.WaitPipeListAsync] - mov [esi+usb_controller.WaitPipeRequestAsync], eax - mov eax, [esi+usb_controller.WaitPipeListPeriodic] - mov [esi+usb_controller.WaitPipeRequestPeriodic], eax -; temporarily stop bulk and interrupt processing; -; this is required for handler of SOF event - and dword [edi+OhciControlReg], not 30h -; remember the frame number when processing has been stopped -; (needs to be done after stopping) - movzx eax, [esi+ohci_controller.FrameNumber-sizeof.ohci_controller] - mov [esi+usb_controller.StartWaitFrame], eax -; make sure that the next SOF will happen after the request - mov dword [edi+OhciInterruptStatusReg], 4 -; enable interrupt on SOF -; Note: OhciInterruptEnableReg/OhciInterruptDisableReg have unusual semantics, -; so there should be 'mov' here, not 'or' - mov dword [edi+OhciInterruptEnableReg], 4 - spin_unlock_irq [esi+usb_controller.RemoveSpinlock] -@@: -; 7. Restore the return value and return. - pop eax - pop edi ebx ; restore used registers to be stdcall - ret -endp - -; Helper procedure for ohci_process_deferred. Processes one completed TD. -; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd. -proc ohci_process_finalized_td -; DEBUGF 1,'K : processing %x\n',ebx -; 1. Check whether the pipe has been closed, either due to API call or due to -; disconnect; if so, the callback will be called by usb_pipe_closed with -; correct status, so go to step 6 with ebx = 0 (do not free the TD). - mov edx, [ebx+usb_gtd.Pipe] - test [edx+usb_pipe.Flags], USB_FLAG_CLOSED - jz @f - lea eax, [ebx+ohci_gtd.NextTD-sizeof.ohci_gtd] - xor ebx, ebx - jmp .next_td2 -@@: -; 2. Remove the descriptor from the descriptors queue. - call usb_unlink_td -; 3. Get number of bytes that remain to be transferred. -; If CurBufPtr is zero, everything was transferred. - xor edx, edx - cmp [ebx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd], edx - jz .gotlen -; Otherwise, the remaining length is -; (BufEnd and 0xFFF) - (CurBufPtr and 0xFFF) + 1, -; plus 0x1000 if BufEnd and CurBufPtr are in different pages. - mov edx, [ebx+ohci_gtd.BufEnd-sizeof.ohci_gtd] - mov eax, [ebx+ohci_gtd.CurBufPtr-sizeof.ohci_gtd] - mov ecx, edx - and edx, 0xFFF - inc edx - xor ecx, eax - and ecx, -0x1000 - jz @f - add edx, 0x1000 -@@: - and eax, 0xFFF - sub edx, eax -.gotlen: -; The actual length is Length - (remaining length). - sub edx, [ebx+usb_gtd.Length] - 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 - 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 -.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 -@@: - pop ebx - lea eax, [ebx+ohci_gtd.NextTD-sizeof.ohci_gtd] -.next_td2: - push ebx - mov ebx, eax - lea edx, [esi+ohci_controller.DoneList-sizeof.ohci_controller] - xor ecx, ecx ; no next item - lock cmpxchg [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], edx - jz .last -; The current item is not the last. -; It is possible, although very rare, that ohci_irq has already advanced -; DoneListEndPtr, but not yet written NextTD. Wait until NextTD is nonzero. -@@: - mov ecx, [ebx] - test ecx, ecx - jz @b -.last: - pop ebx -; ecx = the next item - push ecx -; Free the current item, set ebx to the next item, continue to 5a. - test ebx, ebx - jz @f - stdcall usb1_free_general_td, ebx -@@: - pop ebx - ret -.error: -; 7. There was an error while processing this descriptor. -; The hardware has stopped processing the queue. -; 7a. Save status and length. - push eax - 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] -; DEBUGF 1,'K : %x %x %x %x\n',[ebx-sizeof.ohci_gtd+16],[ebx-sizeof.ohci_gtd+20],[ebx-sizeof.ohci_gtd+24],[ebx-sizeof.ohci_gtd+28] -; mov eax, [ebx+usb_gtd.Pipe] -; DEBUGF 1,'K : pipe: %x %x %x %x\n',[eax-sizeof.ohci_pipe],[eax-sizeof.ohci_pipe+4],[eax-sizeof.ohci_pipe+8],[eax-sizeof.ohci_pipe+12] -; 7b. Traverse the list of descriptors looking for the final packet -; 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 - jnc .found_final - mov ebx, [ebx+usb_gtd.NextVirt] -virtual at esp -.length dd ? -.error_code dd ? -.current_item dd ? -end virtual -.look_final: - call usb_unlink_td - call usb_is_final_packet - jnc .found_final - push [ebx+usb_gtd.NextVirt] - stdcall usb1_free_general_td, ebx - pop ebx - jmp .look_final -.found_final: -; 7c. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets, -; it is not an error. -; Note: all TDs except the last one in any transfer stage are marked -; as short-packet-is-error to stop controller from further processing -; of that stage; we need to restart processing from a TD following the last. -; After that, go to step 5 with eax = 0 (no error). - cmp dword [.error_code], USB_STATUS_UNDERRUN - jnz .no_underrun - test byte [ebx+ohci_gtd.Flags+2-sizeof.ohci_gtd], 1 shl (18-16) - jz .no_underrun - and dword [.error_code], 0 - mov ecx, [ebx+usb_gtd.Pipe] - mov edx, [ecx+ohci_pipe.HeadP-sizeof.ohci_pipe] - and edx, 2 -.advance_queue: - mov eax, [ebx+usb_gtd.NextVirt] - sub eax, sizeof.ohci_gtd - call get_phys_addr - 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 - jmp .notify -; 7d. Abort the entire transfer. -; There are two cases: either there is only one transfer stage -; (everything except control transfers), then ebx points to the last TD and -; all previous TD were unlinked and dismissed (if possible), -; or there are several stages (a control transfer) and ebx points to the last -; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, -; because Setup stage can not produce short packets); for Data stage, we need -; to unlink and free (if possible) one more TD and advance ebx to the next one. -.no_underrun: - cmp [ebx+usb_gtd.Callback], 0 - jnz .halted - cmp ebx, [.current_item] - push [ebx+usb_gtd.NextVirt] - jz @f - stdcall usb1_free_general_td, ebx -@@: - pop ebx - call 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). -; Control pipes normally recover at the next SETUP transaction (first stage -; of any control transfer), so we hope on the best and just advance the queue -; to the next transfer. (According to the standard, "A control pipe may also -; support functional stall as well, but this is not recommended."). -; Advance the transfer queue to the next descriptor. - mov ecx, [ebx+usb_gtd.Pipe] - mov edx, [ecx+ohci_pipe.HeadP-sizeof.ohci_pipe] - and edx, 2 ; keep toggleCarry bit - cmp [ecx+usb_pipe.Type], CONTROL_PIPE - jz @f - inc edx ; set Halted bit -@@: - jmp .advance_queue -endp - -; This procedure is called when a pipe is closing (either due to API call -; or due to disconnect); it unlinks the pipe from the corresponding list. -; esi -> usb_controller, ebx -> usb_pipe -proc ohci_unlink_pipe - cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE - jnz @f - mov eax, [ebx+ohci_pipe.Flags-sizeof.ohci_pipe] - bt eax, 13 - setc cl - bt eax, 11 - setc ch - shr eax, 16 - stdcall usb1_interrupt_list_unlink, eax, ecx -@@: - mov edx, [ebx+usb_pipe.NextVirt] - mov eax, [ebx+usb_pipe.PrevVirt] - mov [edx+usb_pipe.PrevVirt], eax - mov [eax+usb_pipe.NextVirt], edx - mov edx, [ebx+ohci_pipe.NextED-sizeof.ohci_pipe] - mov [eax+ohci_pipe.NextED-sizeof.ohci_pipe], edx - ret -endp diff --git a/kernel/branches/Kolibri-acpi/bus/usb/pipe.inc b/kernel/branches/Kolibri-acpi/bus/usb/pipe.inc index e437fca983..ab249d9675 100644 --- a/kernel/branches/Kolibri-acpi/bus/usb/pipe.inc +++ b/kernel/branches/Kolibri-acpi/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/branches/Kolibri-acpi/bus/usb/protocol.inc b/kernel/branches/Kolibri-acpi/bus/usb/protocol.inc index 3d2fb42625..6d720e380b 100644 --- a/kernel/branches/Kolibri-acpi/bus/usb/protocol.inc +++ b/kernel/branches/Kolibri-acpi/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/branches/Kolibri-acpi/bus/usb/scheduler.inc b/kernel/branches/Kolibri-acpi/bus/usb/scheduler.inc deleted file mode 100644 index 3b4fb592aa..0000000000 --- a/kernel/branches/Kolibri-acpi/bus/usb/scheduler.inc +++ /dev/null @@ -1,1087 +0,0 @@ -; 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). - -; 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. -; -; High-speed scheduler uses the same algorithm as USB1 scheduler: -; when adding a pipe, optimize the following quantity: -; * for every microframe, take all bandwidth scheduled to periodic transfers, -; * calculate maximum over all microframes, -; * select a variant which minimizes that maximum; -; * if there are several such variants, -; prefer those that are closer to end of frame -; to minimize collisions with split transactions; -; when removing a pipe, do nothing (except for bookkeeping). -; in: esi -> usb_controller -; out: edx -> usb_static_ep, eax = S-Mask -proc ehci_select_hs_interrupt_list -; inherit some variables from usb_open_pipe -virtual at ebp-12 -.targetsmask dd ? -.bandwidth dd ? -.target dd ? - dd ? - dd ? -.config_pipe dd ? -.endpoint dd ? -.maxpacket dd ? -.type dd ? -.interval dd ? -end virtual -; prolog, initialize local vars - or [.bandwidth], -1 - or [.target], -1 - or [.targetsmask], -1 - push ebx edi ; save used registers to be stdcall -; 1. In EHCI, every list describes one millisecond = 8 microframes. -; Thus, there are two significantly different branches: -; for pipes with interval >= 8 microframes, advance to 2, -; for pipes which should be planned in every frame (one or more microframes), -; go to 9. -; Note: the actual interval for high-speed devices is 2^([.interval]-1), -; (the core specification forbids [.interval] == 0) - mov ecx, [.interval] - dec ecx - cmp ecx, 3 - jb .every_frame -; 2. Determine the actual interval in milliseconds. - sub ecx, 3 - cmp ecx, 5 ; maximum 32ms - jbe @f - movi ecx, 5 -@@: -; There are four nested loops, -; * Loop #4 (the innermost one) calculates the total periodic bandwidth -; scheduled in the given microframe of the given millisecond. -; * Loop #3 calculates the maximum over all milliseconds -; in the given variant, that is the quantity we're trying to minimize. -; * Loops #1 and #2 check all variants; -; loop #1 is responsible for the target millisecond, -; loop #2 is responsible for the microframe within millisecond. -; 3. Prepare for loops. -; ebx = number of iterations of loop #1 -; [esp] = delta of counter for loop #3, in bytes -; [esp+4] = delta between the first group and the target group, in bytes - movi ebx, 1 - movi edx, sizeof.ehci_static_ep - shl ebx, cl - shl edx, cl - mov eax, 64*sizeof.ehci_static_ep - sub eax, edx - sub eax, edx - push eax - push edx -; 4. Select the best variant. -; 4a. Loop #1: initialize counter = pointer to ehci_static_ep for -; the target millisecond in the first group. - lea edx, [esi+ehci_controller.IntEDs-sizeof.ehci_controller] -.varloop0: -; 4b. Loop #2: initialize counter = microframe within the target millisecond. - xor ecx, ecx -.varloop: -; 4c. Loop #3: save counter of loop #1, -; initialize counter with the value of loop #1 counter, -; initialize maximal bandwidth = zero. - xor edi, edi - push edx -virtual at esp -.saved_counter1 dd ? ; step 4c -.loop3_delta dd ? ; step 3 -.target_delta dd ? ; step 3 -end virtual -.calc_max_bandwidth: -; 4d. Loop #4: initialize counter with the value of loop #3 counter, -; initialize total bandwidth = zero. - xor eax, eax - push edx -.calc_bandwidth: -; 4e. Loop #4: add the bandwidth from the current list -; and advance to the next list, while there is one. - add ax, [edx+ehci_static_ep.Bandwidths+ecx*2] - mov edx, [edx+ehci_static_ep.NextList] - test edx, edx - jnz .calc_bandwidth -; 4f. Loop #4 end: restore counter of loop #3. - pop edx -; 4g. Loop #3: update maximal bandwidth. - cmp eax, edi - jb @f - mov edi, eax -@@: -; 4h. Loop #3: advance the counter and repeat while within the first group. - lea eax, [esi+ehci_controller.IntEDs+32*sizeof.ehci_static_ep-sizeof.ehci_controller] - add edx, [.loop3_delta] - cmp edx, eax - jb .calc_max_bandwidth -; 4i. Loop #3 end: restore counter of loop #1. - pop edx -; 4j. Loop #2: if the current variant is better (maybe not strictly) -; then the previous optimum, update the optimal bandwidth and the target. - cmp edi, [.bandwidth] - ja @f - jb .update - cmp ecx, [.targetsmask] - jb @f -.update: - mov [.bandwidth], edi - mov [.target], edx - mov [.targetsmask], ecx -@@: -; 4k. Loop #2: continue 8 times for every microframe. - inc ecx - cmp ecx, 8 - jb .varloop -; 4l. Loop #1: advance counter and repeat ebx times, -; ebx was calculated in step 3. - add edx, sizeof.ehci_static_ep - dec ebx - jnz .varloop0 -; 5. Calculate bandwidth for the new pipe. - mov eax, [.maxpacket] - call calc_hs_bandwidth - mov ecx, [.maxpacket] - shr ecx, 11 - inc ecx - and ecx, 3 - imul eax, ecx -; 6. Get the pointer to the best list. - pop edx ; restore value from step 3 - pop edx ; get delta calculated in step 3 - add edx, [.target] -; 7. Check that bandwidth for the new pipe plus old bandwidth -; still fits to maximum allowed by the core specification -; current [.bandwidth] + new bandwidth <= limit; -; USB2 specification allows maximum 60000*80% bit times for periodic microframe - mov ecx, [.bandwidth] - add ecx, eax - cmp ecx, 48000 - ja .no_bandwidth -; 8. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return. - mov ecx, [.targetsmask] - add [edx+ehci_static_ep.Bandwidths+ecx*2], ax - add edx, ehci_static_ep.SoftwarePart - movi eax, 1 - shl eax, cl - pop edi ebx ; restore used registers to be stdcall - ret -.no_bandwidth: - dbgstr 'Periodic bandwidth limit reached' - xor eax, eax - xor edx, edx - pop edi ebx - ret -.every_frame: -; The pipe should be scheduled every frame in two or more microframes. -; 9. Calculate maximal bandwidth for every microframe: three nested loops. -; 9a. The outermost loop: ebx = microframe to calculate. - xor ebx, ebx -.calc_all_bandwidths: -; 9b. The intermediate loop: -; edx = pointer to ehci_static_ep in the first group, [esp] = counter, -; edi = maximal bandwidth - lea edx, [esi+ehci_controller.IntEDs-sizeof.ehci_controller] - xor edi, edi - push 32 -.calc_max_bandwidth2: -; 9c. The innermost loop: calculate bandwidth for the given microframe -; in the given frame. - xor eax, eax - push edx -.calc_bandwidth2: - add ax, [edx+ehci_static_ep.Bandwidths+ebx*2] - mov edx, [edx+ehci_static_ep.NextList] - test edx, edx - jnz .calc_bandwidth2 - pop edx -; 9d. The intermediate loop continued: update maximal bandwidth. - cmp eax, edi - jb @f - mov edi, eax -@@: - add edx, sizeof.ehci_static_ep - dec dword [esp] - jnz .calc_max_bandwidth2 - pop eax -; 9e. Push the calculated maximal bandwidth and continue the outermost loop. - push edi - inc ebx - cmp ebx, 8 - jb .calc_all_bandwidths -virtual at esp -.bandwidth7 dd ? -.bandwidth6 dd ? -.bandwidth5 dd ? -.bandwidth4 dd ? -.bandwidth3 dd ? -.bandwidth2 dd ? -.bandwidth1 dd ? -.bandwidth0 dd ? -end virtual -; 10. Select the best variant. -; edx = S-Mask = bitmask of scheduled microframes - movi edx, 0x11 - cmp ecx, 1 - ja @f - mov dl, 0x55 - jz @f - mov dl, 0xFF -@@: -; try all variants edx, edx shl 1, edx shl 2, ... -; while they fit in the lower byte (8 microframes per frame) -.select_best_mframe: - xor edi, edi - mov ecx, edx - mov eax, esp -.calc_mframe: - add cl, cl - jnc @f - cmp edi, [eax] - jae @f - mov edi, [eax] -@@: - add eax, 4 - test cl, cl - jnz .calc_mframe - cmp [.bandwidth], edi - jb @f - mov [.bandwidth], edi - mov [.targetsmask], edx -@@: - add dl, dl - jnc .select_best_mframe -; 11. Restore stack after step 9. - add esp, 8*4 -; 12. Get the pointer to the target list (responsible for every microframe). - lea edx, [esi+ehci_controller.IntEDs.SoftwarePart+62*sizeof.ehci_static_ep-sizeof.ehci_controller] -; 13. Calculate bandwidth on the bus. - mov eax, [.maxpacket] - call calc_hs_bandwidth - mov ecx, [.maxpacket] - shr ecx, 11 - inc ecx - and ecx, 3 - imul eax, ecx -; 14. Check that current [.bandwidth] + new bandwidth <= limit; -; USB2 specification allows maximum 60000*80% bit times for periodic microframe. - mov ecx, [.bandwidth] - add ecx, eax - cmp ecx, 48000 - ja .no_bandwidth -; 15. Update bandwidths including the new pipe. - mov ecx, [.targetsmask] - lea edi, [edx+ehci_static_ep.Bandwidths-ehci_static_ep.SoftwarePart] -.update_bandwidths: - shr ecx, 1 - jnc @f - add [edi], ax -@@: - add edi, 2 - test ecx, ecx - jnz .update_bandwidths -; 16. Return target list and target S-Mask. - mov eax, [.targetsmask] - pop edi ebx ; restore used registers to be stdcall - 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 ehci_hs_interrupt_list_unlink - movzx eax, word [ebx+ehci_pipe.Token-sizeof.ehci_pipe+2] -; calculate bandwidth - call calc_hs_bandwidth - mov ecx, [ebx+ehci_pipe.Flags-sizeof.ehci_pipe] - shr ecx, 30 - imul eax, ecx - movzx ecx, byte [ebx+ehci_pipe.Flags-sizeof.ehci_pipe] -; get target list - mov edx, [ebx+ehci_pipe.BaseList-sizeof.ehci_pipe] -; update bandwidth -.dec_bandwidth: - shr ecx, 1 - jnc @f - sub word [edx+ehci_static_ep.Bandwidths - ehci_static_ep.SoftwarePart], ax -@@: - add edx, 2 - test ecx, ecx - jnz .dec_bandwidth -; return - ret -endp - -; Helper procedure for USB2 scheduler: calculate bandwidth on the bus. -; in: low 11 bits of eax = payload size in bytes -; out: eax = maximal bandwidth in HS-bits -proc calc_hs_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 -; Multiply by 8 for bytes -> bits and then by 7/6 to accomodate bit stuffing; -; total 28/3 = 9+1/3 - mov edx, 55555556h - lea ecx, [eax*9] - mul edx -; Add 989 extra bits: 68 bits for Token packet (32 for SYNC, 24 for token+address, -; 4 extra bits for possible bit stuffing in token+address, 8 for EOP), -; 736 bits for bus turn-around, 40 bits for SYNC+EOP in Data packet, -; 8 bits for inter-packet delay, 49 bits for Handshake packet, -; 88 bits for another inter-packet delay. - lea eax, [ecx+edx+989] - ret -endp - -; Split-transaction scheduler (aka TT scheduler, TT stands for Transaction -; Translator, section 11.14 of the core spec) needs to schedule three event -; types on two buses: Start-Split and Complete-Split on HS bus and normal -; transaction on FS/LS bus. -; Assume that FS/LS bus is more restricted and more important to be scheduled -; uniformly, so select the variant which minimizes maximal used bandwidth -; on FS/LS bus and does not overflow HS bus. -; If there are several such variants, prefer variants which is closest to -; start of frame, and within the same microframe consider HS bandwidth -; utilization as a last criteria. - -; The procedure ehci_select_tt_interrupt_list has been splitted into several -; macro, each representing a logical step of the procedure, -; to simplify understanding what is going on. Consider all the following macro -; as logical parts of one procedure, they are meaningless outside the context. - -; Given a frame, calculate bandwidth occupied by already opened pipes -; in every microframe. -; Look for both HS and FS/LS buses: there are 16 words of information, -; 8 for HS bus, 8 for FS/LS bus, for every microframe. -; Since we count already opened pipes, the total bandwidth in every microframe -; is less than 60000 bits (and even 60000*80% bits), otherwise the scheduler -; would not allow to open those pipes. -; edi -> first list for the frame -macro tt_calc_bandwidth_in_frame -{ -local .lists, .pipes, .pipes_done, .carry -; 1. Zero everything. - xor eax, eax - mov edx, edi -repeat 4 - mov dword [.budget+(%-1)*4], eax -end repeat -repeat 4 - mov dword [.hs_bandwidth+(%-1)*4], eax -end repeat - mov [.total_budget], ax -; Loop over all lists for the given frame. -.lists: -; 2. Total HS bandwidth for all pipes in one list is kept inside list header, -; add it. Note that overflow is impossible, so we may add entire dwords. - mov ebx, [edx+ehci_static_ep.SoftwarePart+usb_static_ep.NextVirt] -repeat 4 - mov eax, dword [edx+ehci_static_ep.Bandwidths+(%-1)*4] - add dword [.hs_bandwidth+(%-1)*4], eax -end repeat -; Loop over all pipes in the given list. - add edx, ehci_static_ep.SoftwarePart -.pipes: - cmp ebx, edx - jz .pipes_done -; 3. For every pipe in every list for the given frame: -; 3a. Check whether the pipe resides on the same FS/LS bus as the new pipe. -; If not, skip this pipe. - mov eax, [ebx+usb_pipe.DeviceData] - mov eax, [eax+usb_device_data.TTHub] - cmp eax, [.tthub] - jnz @f -; 3b. Calculate FS/LS budget for the opened pipe. -; Note that eax = TTHub after 3a. - call tt_calc_budget -; 3c. Update total budget: add the value from 3b -; to the budget of the first microframe scheduled for this pipe. - bsf ecx, [ebx+ehci_pipe.Flags-sizeof.ehci_pipe] - add [.budget+ecx*2], ax -@@: - mov ebx, [ebx+usb_pipe.NextVirt] - jmp .pipes -.pipes_done: - mov edx, [edx+ehci_static_ep.NextList-ehci_static_ep.SoftwarePart] - test edx, edx - jnz .lists -; 4. If the budget for some microframe is exceeded, carry it to the following -; microframe(s). The actual size of one microframe is 187.5 raw bytes; -; the core spec says that 188 bytes should be scheduled in every microframe. - xor eax, eax - xor ecx, ecx -.carry: - xor edx, edx - add ax, [.budget+ecx*2] - cmp ax, 188 - jbe @f - mov dx, ax - mov ax, 188 - sub dx, ax -@@: - mov [.budget+ecx*2], ax - add [.total_budget], ax - mov ax, dx - inc ecx - cmp ecx, 8 - jb .carry -} - -; Checks whether the new pipe fits in the existing FS budget -; starting from the given microframe. If not, mark the microframe -; as impossible for scheduling. -; in: ecx = microframe -macro tt_exclude_microframe_if_no_budget -{ -local .loop, .good, .bad -; 1. If the new budget plus the current budget does not exceed 188 bytes, -; the variant is possible. - mov ax, [.budget+ecx*2] - mov edx, ecx - add ax, [.new_budget] - sub ax, 188 - jbe .good -; 2. Otherwise, -; a) nothing should be scheduled in some following microframes, -; b) after adding the new budget everything should fit in first 6 microframes, -; this guarantees that even in the worst case 90% limit is satisfied. -.loop: - cmp edx, 5 - jae .bad - cmp [.budget+(edx+1)*2], 0 - jnz .bad - inc edx - sub ax, 188 - ja .loop -.bad: - btr [.possible_microframes], ecx -.good: -} - -; Calculate data corresponding to the particular scheduling variant for the new pipe. -; Data describe the current scheduling state collected over all frames touched -; by the given variant: maximal HS bandwidth, maximal FS/LS budget, -; which microframes fit in the current FS/LS budget for all frames. -macro tt_calc_statistics_for_one_variant -{ -local .frames, .microframes -; 1. Initialize: zero maximal bandwidth, -; first 6 microframes are possible for scheduling. - xor eax, eax -repeat 4 - mov dword [.max_hs_bandwidth+(%-1)*4], eax -end repeat - mov [.max_fs_bandwidth], ax - mov [.possible_microframes], 0x3F -; Loop over all frames starting with [.variant] advancing by [.variant_delta]. - mov edi, [.variant] -.frames: -; 2. Calculate statistics for one frame. - tt_calc_bandwidth_in_frame -; 3. Update maximal FS budget. - mov ax, [.total_budget] - cmp ax, [.max_fs_bandwidth] - jb @f - mov [.max_fs_bandwidth], ax -@@: -; 4. For every microframe, update maximal HS bandwidth -; and check whether the microframe is allowed for scheduling. - xor ecx, ecx -.microframes: - mov ax, [.hs_bandwidth+ecx*2] - cmp ax, [.max_hs_bandwidth+ecx*2] - jb @f - mov [.max_hs_bandwidth+ecx*2], ax -@@: - tt_exclude_microframe_if_no_budget - inc ecx - cmp ecx, 8 - jb .microframes -; Stop loop when outside of first descriptor group. - lea eax, [esi+ehci_controller.IntEDs+32*sizeof.ehci_static_ep-sizeof.ehci_controller] - add edi, [.variant_delta] - cmp edi, eax - jb .frames -} - -struct usb_split_info -microframe_mask dd ? ; lower byte is S-mask, second byte is C-mask -ssplit_bandwidth dd ? -csplit_bandwidth dd ? -ends - -; Check whether the current variant and the current microframe are allowed -; for scheduling. If so, check whether they are better than the previously -; selected variant+microframe, if any. If so, update the previously selected -; variant+microframe to current ones. -; ecx = microframe, [.variant] = variant -macro tt_check_variant_microframe -{ -local .nothing, .update, .ssplit, .csplit, .csplit_done -; 1. If the current microframe does not fit in existing FS budget, do nothing. - bt [.possible_microframes], ecx - jnc .nothing -; 2. Calculate maximal HS bandwidth over all affected microframes. -; 2a. Start-split phase: one or more microframes starting with ecx, -; coded in lower byte of .info.microframe_mask. - xor ebx, ebx - xor edx, edx -.ssplit: - lea eax, [ecx+edx] - movzx eax, [.max_hs_bandwidth+eax*2] - add eax, [.info.ssplit_bandwidth] - cmp ebx, eax - ja @f - mov ebx, eax -@@: - inc edx - bt [.info.microframe_mask], edx - jc .ssplit -; 2b. Complete-split phase: zero or more microframes starting with -; ecx+(last start-split microframe)+2, -; coded in second byte of .info.microframe_mask. - add edx, 8 -.csplit: - inc edx - bt [.info.microframe_mask], edx - jnc .csplit_done - lea eax, [ecx+edx] - cmp eax, 8 - jae .csplit_done - movzx eax, [.max_hs_bandwidth+(eax-8)*2] - add eax, [.info.csplit_bandwidth] - cmp ebx, eax - ja .csplit - mov ebx, eax - jmp .csplit -.csplit_done: -; 3. Check that current HS bandwidth + new bandwidth <= limit; -; USB2 specification allows maximum 60000*80% bit times for periodic microframe. - cmp ebx, 48000 - ja .nothing -; 4. This variant is possible for scheduling. -; Check whether it is better than the currently selected one. -; 4a. The primary criteria: FS/LS bandwidth. - mov ax, [.max_fs_bandwidth] - cmp ax, [.best_fs_bandwidth] - ja .nothing - jb .update -; 4b. The secondary criteria: prefer microframes which are closer to start of frame. - cmp ecx, [.targetsmask] - ja .nothing - jb .update -; 4c. The last criteria: HS bandwidth. - cmp ebx, [.bandwidth] - ja .nothing -.update: -; 5. This variant is better than the previously selected. -; Update the best variant with current data. - mov [.best_fs_bandwidth], ax - mov [.bandwidth], ebx - mov [.targetsmask], ecx - mov eax, [.variant] - mov [.target], eax -.nothing: -} - -; TT scheduler: add new pipe. -; in: esi -> usb_controller, edi -> usb_pipe -; out: edx -> usb_static_ep, eax = S-Mask -proc ehci_select_tt_interrupt_list -virtual at ebp-12-.local_vars_size -.local_vars_start: -.info usb_split_info -.new_budget dw ? -.total_budget dw ? -.possible_microframes dd ? -.tthub dd ? -.budget rw 8 -.hs_bandwidth rw 8 -.max_hs_bandwidth rw 8 -.max_fs_bandwidth dw ? -.best_fs_bandwidth dw ? -.variant dd ? -.variant_delta dd ? -.target_delta dd ? -.local_vars_size = $ - .local_vars_start - -.targetsmask dd ? -.bandwidth dd ? -.target dd ? - dd ? - dd ? -.config_pipe dd ? -.endpoint dd ? -.maxpacket dd ? -.type dd ? -.interval dd ? -end virtual - mov eax, [edi+ehci_pipe.Token-sizeof.ehci_pipe] - shr eax, 16 - and eax, (1 shl 11) - 1 - push ebx edi -; 1. Compute the real interval. FS/LS devices encode the interval as -; number of milliseconds. Use the maximal power of two that is not greater than -; the given interval and EHCI scheduling area = 32 frames. - cmp [.interval], 1 - adc [.interval], 0 - mov ecx, 64 - mov eax, 64 * sizeof.ehci_static_ep -@@: - shr ecx, 1 - cmp [.interval], ecx - jb @b - mov [.interval], ecx -; 2. Compute variables for further calculations. -; 2a. [.variant_delta] is delta between two lists from the first group -; that correspond to the same variant. - imul ecx, sizeof.ehci_static_ep - mov [.variant_delta], ecx -; 2b. [.target_delta] is delta between the final answer from the group -; corresponding to [.interval] and the item from the first group. - sub eax, ecx - sub eax, ecx - mov [.target_delta], eax -; 2c. [.variant] is the first list from the first group that corresponds -; to the current variant. - lea eax, [esi+ehci_controller.IntEDs-sizeof.ehci_controller] - mov [.variant], eax -; 2d. [.tthub] identifies TT hub for new pipe, [.new_budget] is FS budget -; for new pipe. - mov eax, [edi+usb_pipe.DeviceData] - mov eax, [eax+usb_device_data.TTHub] - mov ebx, edi - mov [.tthub], eax - call tt_calc_budget - mov [.new_budget], ax -; 2e. [.usb_split_info] describes bandwidth used by new pipe on HS bus. - lea edi, [.info] - call tt_fill_split_info - test eax, eax - jz .no_bandwidth -; 2f. There is no best variant yet, put maximal possible values, -; so any variant would be better than the "current". - or [.best_fs_bandwidth], -1 - or [.target], -1 - or [.bandwidth], -1 - or [.targetsmask], -1 -; 3. Loop over all variants, for every variant decide whether it is acceptable, -; select the best variant from all acceptable variants. -.check_variants: - tt_calc_statistics_for_one_variant - xor ecx, ecx -.check_microframes: - tt_check_variant_microframe - inc ecx - cmp ecx, 6 - jb .check_microframes - add [.variant], sizeof.ehci_static_ep - dec [.interval] - jnz .check_variants -; 4. If there is no acceptable variants, return error. - mov ecx, [.targetsmask] - mov edx, [.target] - cmp ecx, -1 - jz .no_bandwidth -; 5. Calculate the answer: edx -> selected list, eax = S-Mask and C-Mask. - mov eax, [.info.microframe_mask] - add edx, [.target_delta] - shl eax, cl - and eax, 0xFFFF -; 6. Update HS bandwidths in the selected list. - xor ecx, ecx - mov ebx, [.info.ssplit_bandwidth] -.update_ssplit: - bt eax, ecx - jnc @f - add [edx+ehci_static_ep.Bandwidths+ecx*2], bx -@@: - inc ecx - cmp ecx, 8 - jb .update_ssplit - mov ebx, [.info.csplit_bandwidth] -.update_csplit: - bt eax, ecx - jnc @f - add [edx+ehci_static_ep.Bandwidths+(ecx-8)*2], bx -@@: - inc ecx - cmp ecx, 16 - jb .update_csplit -; 7. Return. - add edx, ehci_static_ep.SoftwarePart - pop edi ebx - ret -.no_bandwidth: - dbgstr 'Periodic bandwidth limit reached' - xor eax, eax - 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 ehci_fs_interrupt_list_unlink -; calculate bandwidth - push edi - sub esp, sizeof.usb_split_info - mov edi, esp - call tt_fill_split_info -; get target list - mov edx, [ebx+ehci_pipe.BaseList-sizeof.ehci_pipe] -; update bandwidth for Start-Split - mov eax, [edi+usb_split_info.ssplit_bandwidth] - xor ecx, ecx -.dec_bandwidth_1: - bt [ebx+ehci_pipe.Flags-sizeof.ehci_pipe], ecx - jnc @f - sub word [edx+ecx*2+ehci_static_ep.Bandwidths - ehci_static_ep.SoftwarePart], ax -@@: - inc ecx - cmp ecx, 8 - jb .dec_bandwidth_1 -; update bandwidth for Complete-Split - mov eax, [edi+usb_split_info.csplit_bandwidth] -.dec_bandwidth_2: - bt [ebx+ehci_pipe.Flags-sizeof.ehci_pipe], ecx - jnc @f - sub word [edx+(ecx-8)*2+ehci_static_ep.Bandwidths - ehci_static_ep.SoftwarePart], ax -@@: - inc ecx - cmp ecx, 16 - jb .dec_bandwidth_2 - add esp, sizeof.usb_split_info - pop edi - ret -endp - -; Helper procedure for ehci_select_tt_interrupt_list. -; Calculates "best-case budget" according to the core spec, -; that is, number of bytes (not bits) corresponding to "optimistic" transaction -; time, including inter-packet delays/bus turn-around time, -; but without bit stuffing and timers drift. -; One extra TT-specific delay is added: TT think time from the hub descriptor. -; 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 - mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] - shr eax, 16 - and eax, (1 shl 11) - 1 ; get data length - bt [ebx+ehci_pipe.Token-sizeof.ehci_pipe], 12 - jc .low_speed -; Full-speed interrupt IN/OUT: -; 33 bits for Token packet (8 for SYNC, 24 for token+address, 3 for EOP), -; 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] -; 1 byte is minimal TT think time in addition to ecx. - ret -.low_speed: -; Low-speed interrupt IN/OUT: -; multiply by 8 for LS -> FS, -; 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] - ret -endp - -; Helper procedure for TT scheduler. -; Calculates Start-Split/Complete-Split masks and HS bandwidths. -; ebx -> usb_pipe, edi -> usb_split_info -proc tt_fill_split_info -; Interrupt endpoints. -; The core spec says in 5.7.3 "Interrupt Transfer Packet Size Constraints" that: -; The maximum allowable interrupt data payload size is 64 bytes or less for full-speed. -; Low-speed devices are limited to eight bytes or less maximum data payload size. -; This is important for scheduling, it guarantees that in any case transaction fits -; in two microframes (usually one, two if transaction has started too late in the first -; microframe), so check it. - mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] - mov ecx, 8 - bt eax, 12 - jc @f - mov ecx, 64 -@@: - shr eax, 16 - and eax, (1 shl 11) - 1 ; get data length - cmp eax, ecx - ja .error - add eax, 3 ; add 3 bytes for other fields in data packet, PID+CRC16 -; Multiply by 8 for bytes -> bits and then by 7/6 to accomodate bit stuffing; -; total 28/3 = 9+1/3 - mov edx, 55555556h - lea ecx, [eax*9] - mul edx -; One start-split, three complete-splits (unless the last is too far, -; but this is handled by the caller). - mov eax, [ebx+usb_pipe.LastTD] - mov [edi+usb_split_info.microframe_mask], 0x1C01 -; Structure and HS bandwidth of packets depends on the direction. - bt [eax+ehci_gtd.Token-sizeof.ehci_gtd], 8 - jc .interrupt_in -.interrupt_out: -; Start-Split phase: -; 77 bits for SPLIT packet (32 for SYNC, 8 for EOP, 32 for data, 5 for bit stuffing), -; 88 bits for inter-packet delay, 68 bits for Token packet, -; 88 bits for inter-packet delay, 40 bits for SYNC+EOP in Data packet, -; 88 bits for last inter-packet delay, total 449 bits. - lea eax, [edx+ecx+449] - mov [edi+usb_split_info.ssplit_bandwidth], eax -; Complete-Split phase: -; 77 bits for SPLIT packet, -; 88 bits for inter-packet delay, 68 bits for Token packet, -; 736 bits for bus turn-around, 49 bits for Handshake packet, -; 8 bits for inter-packet delay, total 1026 bits. - mov [edi+usb_split_info.csplit_bandwidth], 1026 - ret -.interrupt_in: -; Start-Split phase: -; 77 bits for SPLIT packet, 88 bits for inter-packet delay, -; 68 bits for Token packet, 88 bits for another inter-packet delay, -; total 321 bits. - mov [edi+usb_split_info.ssplit_bandwidth], 321 -; Complete-Split phase: -; 77 bits for SPLIT packet, 88 bits for inter-packet delay, -; 68 bits for Token packet, 736 bits for bus turn-around, -; 40 bits for SYNC+EOP in Data packet, 8 bits for inter-packet delay, -; total 1017 bits. - lea eax, [edx+ecx+1017] - mov [edi+usb_split_info.csplit_bandwidth], eax - ret -.error: - xor eax, eax - ret -endp diff --git a/kernel/branches/Kolibri-acpi/bus/usb/uhci.inc b/kernel/branches/Kolibri-acpi/bus/usb/uhci.inc deleted file mode 100644 index 6d91550e1a..0000000000 --- a/kernel/branches/Kolibri-acpi/bus/usb/uhci.inc +++ /dev/null @@ -1,1813 +0,0 @@ -; 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. - -; ============================================================================= -; ================================= Constants ================================= -; ============================================================================= -; UHCI register declarations -UhciCommandReg = 0 -UhciStatusReg = 2 -UhciInterruptReg = 4 -UhciFrameNumberReg = 6 -UhciBaseAddressReg = 8 -UhciSOFModifyReg = 0Ch -UhciPort1StatusReg = 10h -; possible PIDs for USB data transfers -USB_PID_SETUP = 2Dh -USB_PID_IN = 69h -USB_PID_OUT = 0E1h -; UHCI does not support an interrupt on root hub status change. We must poll -; the controller periodically. This is the period in timer ticks (10ms). -; We use the value 100 ticks: it is small enough to be responsive to connect -; events and large enough to not load CPU too often. -UHCI_POLL_INTERVAL = 100 -; the following constant is an invalid encoding for length fields in -; uhci_gtd; it is used to check whether an inactive TD has been -; completed (actual length of the transfer is valid) or not processed at all -; (actual length of the transfer is UHCI_INVALID_LENGTH). -; Valid values are 0-4FFh and 7FFh. We use 700h as an invalid value. -UHCI_INVALID_LENGTH = 700h - -; ============================================================================= -; ================================ Structures ================================= -; ============================================================================= - -; UHCI-specific part of a pipe descriptor. -; * The structure corresponds to the Queue Head aka QH from the UHCI -; specification with some additional fields. -; * The hardware uses first two fields (8 bytes). Next two fields are used for -; software book-keeping. -; * The hardware requires 16-bytes alignment of the hardware part. -; Since the allocator (usb_allocate_common) allocates memory sequentially -; from page start (aligned on 0x1000 bytes), block size for the allocator -; must be divisible by 16; usb1_allocate_endpoint ensures this. -struct uhci_pipe -NextQH dd ? -; 1. First bit (bit 0) is Terminate bit. 1 = there is no next QH. -; 2. Next bit (bit 1) is QH/TD select bit. 1 = NextQH points to QH. -; 3. Next two bits (bits 2-3) are reserved. -; 4. With masked 4 lower bits, this is the physical address of the next QH in -; the QH list. -; See also the description before NextVirt field of the usb_pipe -; structure. Additionally to that description, the following is specific for -; the UHCI controller: -; * n=10, N=1024. However, this number is quite large. -; * 1024 lists are used only for individual transfer descriptors for -; Isochronous endpoints. This means that the software can sleep up to 1024 ms -; before initiating the next portion of a large isochronous transfer, which -; is a sufficiently large value. -; * We use the 32ms upper limit for interrupt endpoint polling interval. -; This seems to be a reasonable value. -; * The "next" list for last Periodic list is the Control list. -; * The "next" list for Control list is Bulk list and the "next" -; list for Bulk list is Control list. This loop is used for bandwidth -; reclamation: the hardware traverses lists until end-of-frame. -HeadTD dd ? -; 1. First bit (bit 0) is Terminate bit. 1 = there is no TDs in this QH. -; 2. Next bit (bit 1) is QH/TD select bit. 1 = HeadTD points to QH. -; 3. Next two bits (bits 2-3) are reserved. -; 4. With masked 4 lower bits, this is the physical address of the first TD in -; the TD queue for this QH. -Token dd ? -; This field is a template for uhci_gtd.Token field in transfer -; descriptors. The meaning of individual bits is the same as for -; uhci_gtd.Token, except that PID bitfield is always -; USB_PID_SETUP/IN/OUT for control/in/out pipes, -; the MaximumLength bitfield encodes maximum packet size, -; the Reserved bit 20 is LowSpeedDevice bit. -ErrorTD dd ? -; Usually NULL. If nonzero, it is a pointer to descriptor which was error'd -; and should be freed sometime in the future (the hardware could still use it). -ends - -; This structure describes the static head of every list of pipes. -; The hardware requires 16-bytes alignment of this structure. -; All instances of this structure are located sequentially in uhci_controller, -; uhci_controller is page-aligned, so it is sufficient to make this structure -; 16-bytes aligned and verify that the first instance is 16-bytes aligned -; inside uhci_controller. -struct uhci_static_ep -NextQH dd ? -; Same as uhci_pipe.NextQH. -HeadTD dd ? -; Same as uhci_pipe.HeadTD. -NextList dd ? -; Virtual address of the next list. - dd ? -; Not used. -SoftwarePart rd sizeof.usb_static_ep/4 -; Common part for all controllers, described by usb_static_ep structure. - dd ? -; Padding for 16-byte alignment. -ends - -if sizeof.uhci_static_ep mod 16 -.err uhci_static_ep must be 16-bytes aligned -end if - -; UHCI-specific part of controller data. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 4096 bytes and corresponds to -; the Frame List from UHCI specification. -; * The hardware requires page-alignment of the hardware part, so -; the entire descriptor must be page-aligned. -; This structure is allocated with kernel_alloc (see usb_init_controller), -; this gives page-aligned data. -struct uhci_controller -; ------------------------------ hardware fields ------------------------------ -FrameList rd 1024 -; Entry n corresponds to the head of the frame list to be executed in -; the frames n,n+1024,n+2048,n+3072,... -; The first bit of each entry is Terminate bit, 1 = the frame is empty. -; The second bit of each entry is QH/TD select bit, 1 = the entry points to -; QH, 0 = to TD. -; With masked 2 lower bits, the entry is a physical address of the first QH/TD -; to be executed. -; ------------------------------ software fields ------------------------------ -; Every list has the static head, which is an always empty QH. -; The following fields are static heads, one per list: -; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list. -IntEDs uhci_static_ep - rb 62 * sizeof.uhci_static_ep -ControlED uhci_static_ep -BulkED uhci_static_ep -IOBase dd ? -; Base port in I/O space for UHCI controller. -; UHCI register UhciXxx is addressed as in/out to IOBase + UhciXxx, -; see declarations in the beginning of this source. -DeferredActions dd ? -; Bitmask of bits from UhciStatusReg which need to be processed -; by uhci_process_deferred. Bit 0 = a transaction with IOC bit -; has completed. Bit 1 = a transaction has failed. Set by uhci_irq, -; cleared by uhci_process_deferred. -LastPollTime dd ? -; See the comment before UHCI_POLL_INTERVAL. This variable keeps the -; last time, in timer ticks, when the polling was done. -ends - -if uhci_controller.IntEDs mod 16 -.err Static endpoint descriptors must be 16-bytes aligned inside uhci_controller -end if - -; UHCI general transfer descriptor. -; * The structure describes non-Isochronous data transfers -; for the UHCI controller. -; * The structure includes two parts, the hardware part and the software part. -; * The hardware part consists of first 16 bytes and corresponds to the -; Transfer Descriptor aka TD from UHCI specification. -; * The hardware requires 16-bytes alignment of the hardware part, so -; the entire descriptor must be 16-bytes aligned. Since the allocator -; (uhci_allocate_common) allocates memory sequentially from page start -; (aligned on 0x1000 bytes), block size for the allocator must be -; divisible by 16; usb1_allocate_general_td ensures this. -struct uhci_gtd -NextTD dd ? -; 1. First bit (bit 0) is Terminate bit. 1 = there is no next TD. -; 2. Next bit (bit 1) is QH/TD select bit. 1 = NextTD points to QH. -; This bit is always set to 0 in the implementation. -; 3. Next bit (bit 2) is Depth/Breadth select bit. 1 = the controller should -; proceed to the NextTD after this TD is complete. 0 = the controller -; should proceed to the next endpoint after this TD is complete. -; The implementation sets this bit to 0 for final stages of all transactions -; and to 1 for other stages. -; 4. Next bit (bit 3) is reserved and must be zero. -; 5. With masked 4 lower bits, this is the physical address of the next TD -; in the TD list. -ControlStatus dd ? -; 1. Lower 11 bits (bits 0-10) are ActLen. This is written by the controller -; at the conclusion of a USB transaction to indicate the actual number of -; bytes that were transferred minus 1. -; 2. Next 6 bits (bits 11-16) are reserved. -; 3. Next bit (bit 17) signals Bitstuff error. -; 4. Next bit (bit 18) signals CRC/Timeout error. -; 5. Next bit (bit 19) signals NAK receive. -; 6. Next bit (bit 20) signals Babble error. -; 7. Next bit (bit 21) signals Data Buffer error. -; 8. Next bit (bit 22) signals Stall error. -; 9. Next bit (bit 23) is Active field. 1 = this TD should be processed. -; 10. Next bit (bit 24) is InterruptOnComplete bit. 1 = the controller should -; issue an interrupt on completion of the frame in which this TD is -; executed. -; 11. Next bit (bit 25) is IsochronousSelect bit. 1 = this TD is isochronous. -; 12. Next bit (bit 26) is LowSpeedDevice bit. 1 = this TD is for low-speed. -; 13. Next two bits (bits 27-28) are ErrorCounter field. This field is -; decremented by the controller on every non-fatal error with this TD. -; Babble and Stall are considered fatal errors and immediately deactivate -; the TD without decrementing this field. 0 = no error limit, -; n = deactivate the TD after n errors. -; 14. Next bit (bit 29) is ShortPacketDetect bit. 1 = short packet is an error. -; Note: the specification defines this bit as input for the controller, -; but does not specify the value written by controller. -; Some controllers (e.g. Intel) keep the value, some controllers (e.g. VIA) -; set the value to whether a short packet was actually detected -; (or something like that). -; Thus, we duplicate this bit as bit 0 of OrigBufferInfo. -; 15. Upper two bits (bits 30-31) are reserved. -Token dd ? -; 1. Lower 8 bits (bits 0-7) are PID, one of USB_PID_*. -; 2. Next 7 bits (bits 8-14) are DeviceAddress field. This is the address of -; the target device on the USB bus. -; 3. Next 4 bits (bits 15-18) are Endpoint field. This is the target endpoint -; number. -; 4. Next bit (bit 19) is DataToggle bit. n = issue/expect DATAn token. -; 5. Next bit (bit 20) is reserved. -; 6. Upper 11 bits (bits 21-31) are MaximumLength field. This field specifies -; the maximum number of data bytes for the transfer minus 1 byte. Null data -; packet is encoded as 0x7FF, maximum possible non-null data packet is 1280 -; bytes, encoded as 0x4FF. -Buffer dd ? -; Physical address of the data buffer for this TD. -OrigBufferInfo dd ? -; Usually NULL. If the original buffer crosses a page boundary, this is a -; pointer to the structure uhci_original_buffer for this request. -; bit 0: 1 = short packet is NOT allowed -; (before the TD is processed, it is the copy of bit 29 of ControlStatus; -; some controllers modify that bit, so we need a copy in a safe place) -ends - -; UHCI requires that the entire transfer buffer should be on one page. -; If the actual buffer crosses page boundary, uhci_alloc_packet -; allocates additional memory for buffer for hardware. -; This structure describes correspondence between two buffers. -struct uhci_original_buffer -OrigBuffer dd ? -UsedBuffer dd ? -ends - -; Description of UHCI-specific data and functions for -; controller-independent code. -; Implements the structure usb_hardware_func from hccommon.inc for UHCI. -iglobal -align 4 -uhci_hardware_func: - dd 'UHCI' - dd sizeof.uhci_controller - dd uhci_init - dd uhci_process_deferred - dd uhci_set_device_address - dd uhci_get_device_address - dd uhci_port_disable - dd uhci_new_port.reset - dd uhci_set_endpoint_packet_size - dd usb1_allocate_endpoint - dd uhci_free_pipe - dd uhci_init_pipe - dd uhci_unlink_pipe - dd usb1_allocate_general_td - dd uhci_free_td - dd uhci_alloc_transfer - dd uhci_insert_transfer - dd uhci_new_device -endg - -; ============================================================================= -; =================================== Code ==================================== -; ============================================================================= - -; Controller-specific initialization function. -; Called from usb_init_controller. Initializes the hardware and -; UHCI-specific parts of software structures. -; eax = pointer to uhci_controller to be initialized -; [ebp-4] = pcidevice -proc uhci_init -; inherit some variables from the parent (usb_init_controller) -.devfn equ ebp - 4 -.bus equ ebp - 3 -; 1. Store pointer to uhci_controller for further use. - push eax - mov edi, eax - mov esi, eax -; 2. Initialize uhci_controller.FrameList. -; Note that FrameList is located in the beginning of uhci_controller, -; so esi and edi now point to uhci_controller.FrameList. -; First 32 entries of FrameList contain physical addresses -; of first 32 Periodic static heads, further entries duplicate these. -; See the description of structures for full info. -; Note that all static heads fit in one page, so one call to -; get_phys_addr is sufficient. -if (uhci_controller.IntEDs / 0x1000) <> (uhci_controller.BulkED / 0x1000) -.err assertion failed -end if -; 2a. Get physical address of first static head. -; Note that 1) it is located in the beginning of a page -; and 2) all other static heads fit in the same page, -; so one call to get_phys_addr without correction of lower 12 bits -; is sufficient. -if (uhci_controller.IntEDs mod 0x1000) <> 0 -.err assertion failed -end if - add eax, uhci_controller.IntEDs - call get_phys_addr -; 2b. Fill first 32 entries. - inc eax - inc eax ; set QH bit for uhci_pipe.NextQH - movi ecx, 32 - mov edx, ecx -@@: - stosd - add eax, sizeof.uhci_static_ep - loop @b -; 2c. Fill the rest entries. - mov ecx, 1024 - 32 - rep movsd -; 3. Initialize static heads uhci_controller.*ED. -; Use the loop over groups: first group consists of first 32 Periodic -; descriptors, next group consists of next 16 Periodic descriptors, -; ..., last group consists of the last Periodic descriptor. -; 3a. Prepare for the loop. -; make esi point to the second group, other registers are already set. - add esi, 32*4 + 32*sizeof.uhci_static_ep -; 3b. Loop over groups. On every iteration: -; edx = size of group, edi = pointer to the current group, -; esi = pointer to the next group, eax = physical address of the next group. -.init_static_eds: -; 3c. Get the size of next group. - shr edx, 1 -; 3d. Exit the loop if there is no next group. - jz .init_static_eds_done -; 3e. Initialize the first half of the current group. -; Advance edi to the second half. - push eax esi - call uhci_init_static_ep_group - pop esi eax -; 3f. Initialize the second half of the current group -; with the same values. -; Advance edi to the next group, esi/eax to the next of the next group. - call uhci_init_static_ep_group - jmp .init_static_eds -.init_static_eds_done: -; 3g. Initialize the last static head. - xor esi, esi - call uhci_init_static_endpoint -; 3i. Initialize the head of Control list. - add eax, sizeof.uhci_static_ep - call uhci_init_static_endpoint -; 3j. Initialize the head of Bulk list. - sub eax, sizeof.uhci_static_ep - 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 - push eax -; 4b. Disable IO access. - and al, not 1 - push ecx - xchg eax, ecx - call pci_write_reg - pop ecx -; 4c. Read&save IO base address. - mov eax, ecx - mov bl, 20h - call pci_read_reg - 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 -; 4e. Read IO base address. - mov eax, ecx - call pci_read_reg - and al, not 3 - cwde - not eax - inc eax - 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 -; 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 -; 5. Reset the controller. -; 5e. Host reset. - mov edx, edi - mov ax, 2 - out dx, ax -; 5f. Wait up to 10ms. - movi ecx, 10 -@@: - push esi - movi esi, 1 - call delay_ms - pop esi - in ax, dx - test al, 2 - loopnz @b - jz @f - dbgstr 'UHCI controller reset timeout' - jmp .fail -@@: -if 0 -; emergency variant for tests - always wait 10 ms -; wait 10 ms - push esi - movi esi, 10 - call delay_ms - pop esi -; clear reset signal - xor eax, eax - out dx, ax -end if -.resetok: -; 6. Get number of ports & disable all ports. - add esi, edi - lea edx, [edi+UhciPort1StatusReg] -.scanports: - cmp edx, esi - jae .doneports - in ax, dx - cmp ax, 0xFFFF - jz .doneports - test al, al - jns .doneports - xor eax, eax - out dx, ax - inc edx - inc edx - jmp .scanports -.doneports: - lea esi, [edx-UhciPort1StatusReg] - sub esi, edi - shr esi, 1 ; esi = number of ports - jnz @f - dbgstr 'error: no ports on UHCI controller' - jmp .fail -@@: -; 7. Setup the rest of uhci_controller. - xchg esi, [esp] ; restore the pointer to uhci_controller from the step 1 - add esi, sizeof.uhci_controller - 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] - 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 -; 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. - xor eax, eax - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] -; 9a. 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. - inc edx - inc edx ; UhciInterruptReg == 4 - mov al, 0Dh - out dx, ax -; 9c. UhciFrameNumberReg := 0. - inc edx - inc edx ; UhciFrameNumberReg == 6 - mov al, 0 - out dx, ax -; 9d. UhciBaseAddressReg := physical address of uhci_controller. - inc edx - inc edx ; UhciBaseAddressReg == 8 - lea eax, [esi-sizeof.uhci_controller] - call get_phys_addr - out dx, eax -; 9e. 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. - call uhci_poll_roothub -; 11. 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. - pop ecx - xor eax, eax - ret -endp - -; Controller-specific pre-initialization function: take ownership from BIOS. -; UHCI has no mechanism to ask the owner politely to release ownership, -; 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 - and eax, 0xFFFC - xchg eax, edx -; 2. Stop the controller and disable all interrupts. - in ax, dx - and al, not 1 - out dx, ax - add edx, UhciInterruptReg - xor eax, eax - 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 - ret -endp - -; Helper procedure for step 3 of uhci_init. -; Initializes the static head of one list. -; eax = physical address of the "next" list, esi = pointer to the "next" list, -; edi = pointer to head to initialize. -; Advances edi to the next head, keeps eax/esi. -proc uhci_init_static_endpoint - mov [edi+uhci_static_ep.NextQH], eax - 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 - add edi, sizeof.uhci_static_ep - uhci_static_ep.SoftwarePart - ret -endp - -; Helper procedure for step 3 of uhci_init, see comments there. -; Initializes one half of group of static heads. -; edx = size of the next group = half of size of the group, -; edi = pointer to the group, eax = physical address of the next group, -; esi = pointer to the next group. -; Advances eax, esi, edi to next group, keeps edx. -proc uhci_init_static_ep_group - push edx -@@: - call uhci_init_static_endpoint - add eax, sizeof.uhci_static_ep - add esi, sizeof.uhci_static_ep - dec edx - jnz @b - pop edx - ret -endp - -; IRQ handler for UHCI controllers. -uhci_irq.noint: -; Not our interrupt: restore esi and return zero. - pop esi - xor eax, eax - ret -proc uhci_irq - push esi ; save used register to be cdecl -virtual at esp - dd ? ; saved esi - dd ? ; return address -.controller dd ? -end virtual - mov esi, [.controller] -; 1. Read UhciStatusReg. - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] - inc edx - inc edx ; UhciStatusReg == 2 - in ax, dx -; 2. Test whether it is our interrupt; if so, at least one status bit is set. - test al, 0x1F - jz .noint -; 3. Clear all status bits. - out dx, ax -; 4. Sanity check. - test al, 0x3C - jz @f - DEBUGF 1,'K : something terrible happened with UHCI (%x)\n',al -@@: -; 5. We can't do too much from an interrupt handler, e.g. we can't take -; any mutex locks since our code could be called when another code holds the -; lock and has no chance to release it. Thus, only inform the processing thread -; that it should scan the queue and wake it if needed. - lock or byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], al - push ebx - xor ebx, ebx - inc ebx - call usb_wakeup_if_needed - pop ebx -; 6. This is our interrupt; return 1. - mov al, 1 - pop esi ; restore used register to be stdcall - ret -endp - -; This procedure is called in the USB thread from usb_thread_proc, -; processes regular actions and those actions which can't be safely done -; from interrupt handler. -; Returns maximal time delta before the next call. -proc uhci_process_deferred - push ebx edi ; save used registers to be stdcall -; 1. Initialize the return value. - push -1 -; 2. Poll the root hub every UHCI_POLL_INTERVAL ticks. -; Also force polling if some transaction has completed with errors; -; 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] - sub eax, [esi+uhci_controller.LastPollTime-sizeof.uhci_controller] - sub eax, UHCI_POLL_INTERVAL - jl .nopoll -.force_poll: - mov eax, [timer_ticks] - mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax - call uhci_poll_roothub - mov eax, -UHCI_POLL_INTERVAL -.nopoll: - neg eax - cmp [esp], eax - jb @f - mov [esp], eax -@@: -; 3. Process wait lists. -; 3a. Test whether there is a wait request. - mov eax, [esi+usb_controller.WaitPipeRequestAsync] - cmp eax, [esi+usb_controller.ReadyPipeHeadAsync] - jnz .check_removed - mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] - cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic] - jz @f -.check_removed: -; 3b. Yep. Find frame and compare it with the saved one. - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] - add edx, UhciFrameNumberReg - in ax, dx - cmp word [esi+usb_controller.StartWaitFrame], ax - jnz .removed -; 3c. The same frame; wake up in 0.01 sec. - mov dword [esp], 1 - jmp @f -.removed: -; 3d. The frame is changed, old contents is guaranteed to be forgotten. - mov eax, [esi+usb_controller.WaitPipeRequestAsync] - mov [esi+usb_controller.ReadyPipeHeadAsync], eax - mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] - mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax -@@: -; 4. Process disconnect events. This should be done after step 2 -; (which includes the first stage of disconnect processing). - call 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 - cmp [esi+usb_controller.NewConnected], ecx - jz .skip_newconnected -.portloop: - bt [esi+usb_controller.NewConnected], ecx - jnc .noconnect - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ConnectedTime+ecx*4] - sub eax, USB_CONNECT_DELAY - jge .connected - neg eax - cmp [esp], eax - jb .nextport - mov [esp], eax - jmp .nextport -.connected: - btr [esi+usb_controller.NewConnected], ecx - call uhci_new_port -.noconnect: -.nextport: - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb .portloop -.skip_newconnected: -; 6. Test for processed packets. -; This should be done after step 4, so transfers which were failed due -; to disconnect are marked with the exact reason, not just -; 'device not responding'. - xor eax, eax - xchg byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], al - test al, 3 - jz .noioc - call uhci_process_updated_schedule -.noioc: -; 7. Test whether reset signalling has been started. If so, -; either should be stopped now (if time is over) or schedule wakeup (otherwise). -; This should be done after step 6, because a completed SET_ADDRESS command -; could result in reset of a new port. -.test_reset: -; 7a. Test whether reset signalling is active. - cmp [esi+usb_controller.ResettingStatus], 1 - jnz .no_reset_in_progress -; 7b. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ResetTime] - sub eax, USB_RESET_TIME - jge .reset_done -; 7c. Not yet, but initiate wakeup in -eax ticks and exit this step. - neg eax - cmp [esp], eax - jb .skip_reset - mov [esp], eax - jmp .skip_reset -.reset_done: -; 7d. Yep, call the worker function and proceed to 7e. - call uhci_port_reset_done -.no_reset_in_progress: -; 7e. Test whether reset process is done, either successful or failed. - cmp [esi+usb_controller.ResettingStatus], 0 - jz .skip_reset -; 7f. Yep. Test whether it should be stopped. - mov eax, [timer_ticks] - sub eax, [esi+usb_controller.ResetTime] - sub eax, USB_RESET_RECOVERY_TIME - jge .reset_recovery_done -; 7g. Not yet, but initiate wakeup in -eax ticks and exit this step. - neg eax - cmp [esp], eax - jb .skip_reset - mov [esp], eax - jmp .skip_reset -.reset_recovery_done: -; 7h. Yep, call the worker function. This could initiate another reset, -; so return to the beginning of this step. - call uhci_port_init - jmp .test_reset -.skip_reset: -; 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 -; 8b. If no new requests, skip the rest of this step. - test eax, eax - jz @f -; 8c. UHCI is not allowed to cache anything; we don't know what is -; processed right now, but we can be sure that the controller will not -; use any removed structure starting from the next frame. -; Request removal of everything disconnected until now, -; schedule wakeup in 0.01 sec. - mov eax, [esi+usb_controller.WaitPipeListAsync] - mov [esi+usb_controller.WaitPipeRequestAsync], eax - mov eax, [esi+usb_controller.WaitPipeListPeriodic] - mov [esi+usb_controller.WaitPipeRequestPeriodic], eax - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] - add edx, UhciFrameNumberReg - in ax, dx - mov word [esi+usb_controller.StartWaitFrame], ax - mov dword [esp], 1 -@@: -; 9. Return the value from the top of stack. - pop eax - pop edi ebx ; restore used registers to be stdcall. - ret -endp - -; This procedure is called in the USB thread from uhci_process_deferred -; when UHCI IRQ handler has signalled that new IOC-packet was processed. -; It scans all lists for completed packets and calls uhci_process_finalized_td -; for those packets. -; in: esi -> usb_controller -proc uhci_process_updated_schedule -; Important note: we cannot hold the list lock during callbacks, -; because callbacks sometimes open and/or close pipes and thus acquire/release -; the corresponding lock itself. -; Fortunately, pipes can be finally freed only by another step of -; uhci_process_deferred, so all pipes existing at the start of this function -; will be valid while this function is running. Some pipes can be removed -; from the corresponding list, some pipes can be inserted; insert/remove -; functions guarantee that traversing one list yields all pipes that were in -; that list at the beginning of the traversing (possibly with some new pipes, -; possibly without some new pipes, that doesn't matter). -; 1. Process all Periodic lists. - lea edi, [esi+uhci_controller.IntEDs.SoftwarePart-sizeof.uhci_controller] - lea ebx, [esi+uhci_controller.IntEDs.SoftwarePart+63*sizeof.uhci_static_ep-sizeof.uhci_controller] -@@: - call uhci_process_updated_list - cmp edi, ebx - jnz @b -; 2. Process the Control list. - call uhci_process_updated_list -; 3. Process the Bulk list. - call uhci_process_updated_list -; 4. Return. - ret -endp - -; This procedure is called from uhci_process_updated_schedule, -; see comments there. -; It processes one list, esi -> usb_controller, edi -> usb_static_ep, -; and advances edi to the next head. -proc uhci_process_updated_list - push ebx ; save used register to be stdcall -; 1. Perform the external loop over all pipes. - mov ebx, [edi+usb_static_ep.NextVirt] -.loop: - cmp ebx, edi - jz .done -; store pointer to the next pipe in the stack - push [ebx+usb_static_ep.NextVirt] -; 2. For every pipe, perform the internal loop over all descriptors. -; All descriptors are organized in the queue; we process items from the start -; 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 - mov ebx, [ebx+usb_pipe.LastTD] - push ebx - mov ebx, [ebx+usb_gtd.NextVirt] -.tdloop: -; 3. For every descriptor, test active flag and check for end-of-queue; -; if either of conditions holds, exit from the internal loop. - cmp ebx, [esp] - jz .tddone - mov eax, [ebx+uhci_gtd.ControlStatus-sizeof.uhci_gtd] - test eax, 1 shl 23 ; active? - jnz .tddone -; Release the queue lock while processing one descriptor: -; callback function could (and often would) schedule another transfer. - push ecx - call mutex_unlock - call uhci_process_finalized_td - pop ecx - call mutex_lock - jmp .tdloop -.tddone: - call mutex_unlock - pop ebx -; End of internal loop, restore pointer to the next pipe -; and continue the external loop. - pop ebx - jmp .loop -.done: - pop ebx ; restore used register to be stdcall - add edi, sizeof.uhci_static_ep - ret -endp - -; This procedure is called from uhci_process_updated_list, which is itself -; called from uhci_process_updated_schedule, see comments there. -; It processes one completed descriptor. -; 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 -; 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] -; 2. If this is IN transfer into special buffer, copy the data -; to target location. - mov edx, [ebx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] - and edx, not 1 ; clear lsb (used for another goal) - jz .nocopy - cmp byte [ebx+uhci_gtd.Token-sizeof.uhci_gtd], USB_PID_IN - jnz .nocopy -; Note: we assume that pointer to buffer is valid in the memory space of -; the USB thread. This means that buffer must reside in kernel memory -; (shared by all processes). - push esi edi - mov esi, [edx+uhci_original_buffer.UsedBuffer] - mov edi, [edx+uhci_original_buffer.OrigBuffer] - mov ecx, [ebx+uhci_gtd.ControlStatus-sizeof.uhci_gtd] - inc ecx - and ecx, 7FFh - mov edx, ecx - shr ecx, 2 - and edx, 3 - rep movsd - mov ecx, edx - rep movsb - pop edi esi -.nocopy: -; 3. Calculate actual number of bytes transferred. -; 3a. Read the state. - mov eax, [ebx+uhci_gtd.ControlStatus-sizeof.uhci_gtd] - mov ecx, [ebx+uhci_gtd.Token-sizeof.uhci_gtd] -; 3b. Get number of bytes processed. - lea edx, [eax+1] - and edx, 7FFh -; 3c. Subtract number of bytes in this packet. - add ecx, 1 shl 21 - shr ecx, 21 - sub edx, ecx -; 3d. Add total length transferred so far. - add edx, [ebx+usb_gtd.Length] -; Actions on error and on success are slightly different. -; 4. Test for error. On error, proceed to step 5, otherwise go to step 6 -; with ecx = 0 (no error). -; USB transaction error is always considered as such. -; If short packets are not allowed, UHCI controllers do not set an error bit, -; but stop (clear Active bit and do not advance) the queue. -; Short packet is considered as an error if the packet is actually short -; (actual length is less than maximal one) and the code creating the packet -; requested that behaviour (so bit 0 of OrigBufferInfo is set; this could be -; because the caller disallowed short packets or because the packet is not -; the last one in the corresponding transfer). - xor ecx, ecx - test eax, 1 shl 22 - jnz .error - test byte [ebx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 1 - jz .notify - cmp edx, [ebx+usb_gtd.Length] - jz .notify -.error: -; 5. There was an error while processing this packet. -; The hardware has stopped processing the queue. - DEBUGF 1,'K : TD failed:\n' -if sizeof.uhci_gtd <> 20 -.err modify offsets for debug output -end if - 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] -; 5a. Save the status and length. - push edx - push eax - mov eax, [ebx+usb_gtd.Pipe] - DEBUGF 1,'K : pipe: %x %x\n',[eax+0-sizeof.uhci_pipe],[eax+4-sizeof.uhci_pipe] -; 5b. Store the current TD as an error packet. -; If an error packet is already stored for this pipe, -; it is definitely not used already, so free the old packet. - mov eax, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe] - test eax, eax - jz @f - stdcall uhci_free_td, eax -@@: - mov eax, [ebx+usb_gtd.Pipe] - mov [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe], ebx -; 5c. Traverse the list of descriptors looking for the final packet -; 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 - jnc .found_final - mov ebx, [ebx+usb_gtd.NextVirt] -.look_final: - call usb_unlink_td - call usb_is_final_packet - jnc .found_final - push [ebx+usb_gtd.NextVirt] - stdcall uhci_free_td, ebx - pop ebx - jmp .look_final -.found_final: -; 5d. Restore the status saved in 5a and transform it to the error code. - pop eax ; error code - shr eax, 16 -; Notes: -; * any USB transaction error results in Stalled bit; if it is not set, -; but we are here, it must be due to short packet; -; * babble is considered a fatal USB transaction error, -; other errors just lead to retrying the transaction; -; if babble is detected, return the corresponding error; -; * if several non-fatal errors have occured during transaction retries, -; all corresponding bits are set. In this case, return some error code, -; the order is quite arbitrary. - movi ecx, USB_STATUS_UNDERRUN - test al, 1 shl (22-16) ; not Stalled? - jz .know_error - mov cl, USB_STATUS_OVERRUN - test al, 1 shl (20-16) ; Babble detected? - jnz .know_error - mov cl, USB_STATUS_BITSTUFF - test al, 1 shl (17-16) ; Bitstuff error? - jnz .know_error - mov cl, USB_STATUS_NORESPONSE - test al, 1 shl (18-16) ; CRC/TimeOut error? - jnz .know_error - mov cl, USB_STATUS_BUFOVERRUN - test al, 1 shl (21-16) ; Data Buffer error? - jnz .know_error - mov cl, USB_STATUS_STALL -.know_error: -; 5e. If error code is USB_STATUS_UNDERRUN -; and the last TD allows short packets, it is not an error. -; Note: all TDs except the last one in any transfer stage are marked -; as short-packet-is-error to stop controller from further processing -; of that stage; we need to restart processing from a TD following the last. -; After that, go to step 6 with ecx = 0 (no error). - cmp ecx, USB_STATUS_UNDERRUN - jnz @f - test byte [ebx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 1 - jnz @f -; The controller has stopped this queue on the error packet. -; Update uhci_pipe.HeadTD to point to the next packet in the queue. - call uhci_fix_toggle - xor ecx, ecx -.control: - mov eax, [ebx+uhci_gtd.NextTD-sizeof.uhci_gtd] - and al, not 0xF - mov edx, [ebx+usb_gtd.Pipe] - mov [edx+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax - pop edx ; length - jmp .notify -@@: -; 5f. Abort the entire transfer. -; There are two cases: either there is only one transfer stage -; (everything except control transfers), then ebx points to the last TD and -; all previous TD were unlinked and dismissed (if possible), -; or there are several stages (a control transfer) and ebx points to the last -; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, -; because Setup stage can not produce short packets); for Data stage, we need -; to unlink and free (if possible) one more TD and advance ebx to the next one. - cmp [ebx+usb_gtd.Callback], 0 - jnz .normal -; We cannot free ErrorTD yet, it could still be used by the hardware. - push ecx - mov eax, [ebx+usb_gtd.Pipe] - push [ebx+usb_gtd.NextVirt] - cmp ebx, [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe] - jz @f - stdcall uhci_free_td, ebx -@@: - pop ebx - call usb_unlink_td - pop ecx -.normal: -; 5g. For bulk/interrupt transfers we have no choice but halt the queue, -; the driver should intercede (through some API which is not written yet). -; Control pipes normally recover at the next SETUP transaction (first stage -; of any control transfer), so we hope on the best and just advance the queue -; to the next transfer. (According to the standard, "A control pipe may also -; support functional stall as well, but this is not recommended."). - mov edx, [ebx+usb_gtd.Pipe] - cmp [edx+usb_pipe.Type], CONTROL_PIPE - jz .control -; Bulk/interrupt transfer; halt the queue. - mov eax, [ebx+uhci_gtd.NextTD-sizeof.uhci_gtd] - and al, not 0xF - inc eax ; set Halted bit - mov [edx+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax - pop edx ; restore length saved in step 5a -.notify: -; 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: -; 7. Free the current descriptor (if allowed) and return the next one. -; 7a. Save pointer to the next descriptor. - push [ebx+usb_gtd.NextVirt] -; 7b. Free the descriptor, unless it is saved as ErrorTD. - mov eax, [ebx+usb_gtd.Pipe] - cmp [eax+uhci_pipe.ErrorTD-sizeof.uhci_pipe], ebx - jz @f - stdcall uhci_free_td, ebx -@@: -; 7c. Restore pointer to the next descriptor and return. - pop ebx - ret -endp - -; Helper procedure for restarting transfer queue. -; When transfers are queued, their toggle bit is filled assuming that -; everything will go without errors. On error, some packets needs to be -; skipped, so toggle bits may become incorrect. -; This procedure fixes toggle bits. -; in: ebx -> last packet to be skipped, ErrorTD -> last processed packet -proc uhci_fix_toggle -; 1. Nothing to do for control pipes: in that case, -; toggle bits for different transfer stages are independent. - mov ecx, [ebx+usb_gtd.Pipe] - cmp [ecx+usb_pipe.Type], CONTROL_PIPE - jz .nothing -; 2. The hardware expects next packet with toggle = (ErrorTD.toggle xor 1), -; the current value in next packet is (ebx.toggle xor 1). -; Nothing to do if ErrorTD.toggle == ebx.toggle. - mov eax, [ecx+uhci_pipe.ErrorTD-sizeof.uhci_pipe] - mov eax, [eax+uhci_gtd.Token-sizeof.uhci_gtd] - xor eax, [ebx+uhci_gtd.Token-sizeof.uhci_gtd] - test eax, 1 shl 19 - jz .nothing -; 3. Lock the transfer queue. - add ecx, usb_pipe.Lock - call mutex_lock -; 4. Flip the toggle bit in all packets from ebx.NextVirt to ecx.LastTD -; (inclusive). - mov eax, [ebx+usb_gtd.NextVirt] -.loop: - xor byte [eax+uhci_gtd.Token-sizeof.uhci_gtd+2], 1 shl (19-16) - cmp eax, [ecx+usb_pipe.LastTD-usb_pipe.Lock] - mov eax, [eax+usb_gtd.NextVirt] - jnz .loop -; 5. Flip the toggle bit in uhci_pipe structure. - 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 -.nothing: - ret -endp - -; This procedure is called in the USB thread from uhci_process_deferred -; every UHCI_POLL_INTERVAL ticks. It polls the controller for -; connect/disconnect events. -; in: esi -> usb_controller -proc uhci_poll_roothub - push ebx ; save used register to be stdcall -; 1. Prepare for the loop for every port. - xor ecx, ecx -.portloop: -; 2. Some implementations of UHCI set ConnectStatusChange bit in a response to -; PortReset. Thus, we must ignore this change for port which is resetting. - cmp cl, [esi+usb_controller.ResettingPort] - jz .nextport -; 3. Read port status. - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] - lea edx, [edx+ecx*2+UhciPort1StatusReg] - in ax, dx -; 4. If no change bits are set, continue to the next port. - test al, 0Ah - jz .nextport -; 5. Clear change bits and read the status again. -; (It is possible, although quite unlikely, that some event occurs between -; the first read and the clearing, invalidating the old status. If an event -; occurs after the clearing, we will not miss it, looking in the next scan. - out dx, ax - mov ebx, eax - in ax, dx -; 6. Process connect change notifications. -; Note: if connect status has changed, ignore enable status change; -; it is normal to disable a port at disconnect event. -; 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 -; 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] - mov [esi+usb_controller.ConnectedTime+ecx*4], eax - bts [esi+usb_controller.NewConnected], ecx - jmp .nextport -.disconnect: - btr [esi+usb_controller.NewConnected], ecx - jmp .nextport -.noconnectchange: -; 7. Process enable change notifications. -; Note: that needs work. - test bl, 8 - jz .nextport - test al, 4 - jnz .nextport - dbgstr 'Port disabled' -.nextport: -; 8. Continue the loop for every port. - inc ecx - cmp ecx, [esi+usb_controller.NumPorts] - jb .portloop - pop ebx ; restore used register to be stdcall - ret -endp - -; This procedure is called from uhci_process_deferred when -; a new device was connected at least USB_CONNECT_DELAY ticks -; and therefore is ready to be configured. -; in: esi -> usb_controller, ecx = port (zero-based) -proc uhci_new_port -; test whether we are configuring another port -; if so, postpone configuring and return - bts [esi+usb_controller.PendingPorts], ecx - cmp [esi+usb_controller.ResettingPort], -1 - jnz .nothing - btr [esi+usb_controller.PendingPorts], ecx -; fall through to uhci_new_port.reset - -; This function is called from uhci_new_port and uhci_test_pending_port. -; It starts reset signalling for the port. Note that in USB first stages -; of configuration can not be done for several ports in parallel. -.reset: -; 1. Store information about resetting hub (roothub) and port. - and [esi+usb_controller.ResettingHub], 0 - mov [esi+usb_controller.ResettingPort], cl -; 2. Initiate reset signalling. - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] - lea edx, [edx+ecx*2+UhciPort1StatusReg] - in ax, dx - or ah, 2 - out dx, ax -; 3. Store the current time and set status to 1 = reset signalling active. - mov eax, [timer_ticks] - mov [esi+usb_controller.ResetTime], eax - mov [esi+usb_controller.ResettingStatus], 1 -.nothing: - ret -endp - -; This procedure is called from uhci_process_deferred when -; reset signalling for a port needs to be finished. -proc uhci_port_reset_done -; 1. Stop reset signalling. - movzx ecx, [esi+usb_controller.ResettingPort] - 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 - 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 - pop esi -; 3. ConnectStatus bit is zero during reset and becomes 1 during step 2; -; some controllers interpret this as a (fake) connect event. -; Enable port and clear status change notification. - in ax, dx - DEBUGF 1,'%x\n',ax - 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] - mov [esi+usb_controller.ResetTime], eax - mov [esi+usb_controller.ResettingStatus], 2 - ret -endp - -; This procedure is called from uhci_process_deferred when -; a new device has been reset, recovered after reset and -; needs to be configured. -; in: esi -> usb_controller -proc uhci_port_init -; 1. Read port status. - mov [esi+usb_controller.ResettingStatus], 0 - movzx ecx, [esi+usb_controller.ResettingPort] - 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 -; 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 -@@: -; 3. Copy LowSpeed bit to bit 0 of eax and call the worker procedure -; to notify the protocol layer about new UHCI device. - push edx - mov al, ah - call uhci_new_device - pop edx - test eax, eax - jnz .nothing -; 4. If something at the protocol layer has failed -; (no memory, no bus address), disable the port and stop the initialization. -.disable_exit: - in ax, dx - and al, not 4 - out dx, ax ; disable the port - jmp usb_test_pending_port -.nothing: - ret -endp - -; This procedure is called from uhci_port_init and from hub support code -; when a new device is connected and has been reset. -; It calls usb_new_device at the protocol layer with correct parameters. -; in: esi -> usb_controller, eax = speed; -; UHCI is USB1 device, so only low bit of eax (LowSpeed) is used. -proc uhci_new_device -; 1. Clear all bits of speed except bit 0. - and eax, 1 -; 2. Store the speed for the protocol layer. - mov [esi+usb_controller.ResettingSpeed], al -; 3. Create pseudo-pipe in the stack. -; See uhci_init_pipe: only .Controller and .Token fields are used. - push esi ; fill .Controller field - mov ecx, esp - shl eax, 20 ; bit 20 = LowSpeedDevice - push eax ; ignored (ErrorTD) - push eax ; .Token field: DeviceAddress is zero, bit 20 = LowSpeedDevice -; 4. Notify the protocol layer. - call usb_new_device -; 5. Cleanup the stack after step 3 and return. - add esp, 12 - ret -endp - -; This procedure is called from usb_set_address_callback -; and stores USB device address in the uhci_pipe structure. -; 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 -endp - -; This procedure returns USB device address from the uhci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe -; out: eax = endpoint address -proc uhci_get_device_address - mov al, byte [ebx+uhci_pipe.Token+1-sizeof.uhci_pipe] - and eax, 7Fh - ret -endp - -; This procedure is called from usb_set_address_callback -; if the device does not accept SET_ADDRESS command and needs -; to be disabled at the port level. -; in: esi -> usb_controller, ecx = port (zero-based) -proc uhci_port_disable - mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller] - lea edx, [edx+UhciPort1StatusReg+ecx*2] - in ax, dx - and al, not 4 - out dx, ax - ret -endp - -; This procedure is called from usb_get_descr8_callback when -; the packet size for zero endpoint becomes known and -; stores the packet size in uhci_pipe structure. -; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size -proc uhci_set_endpoint_packet_size - dec ecx - shl ecx, 21 - and [ebx+uhci_pipe.Token-sizeof.uhci_pipe], (1 shl 21) - 1 - 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 -endp - -; This procedure is called from API usb_open_pipe and processes -; the controller-specific part of this API. See docs. -; 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 -proc uhci_init_pipe -; inherit some variables from the parent usb_open_pipe -virtual at ebp-12 -.speed db ? - rb 3 -.bandwidth dd ? -.target dd ? - rd 2 -.config_pipe dd ? -.endpoint dd ? -.maxpacket dd ? -.type dd ? -.interval dd ? -end virtual -; 1. Initialize ErrorTD to zero. - and [edi+uhci_pipe.ErrorTD-sizeof.uhci_pipe], 0 -; 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 - mov [edi+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax -; 3. Initialize Token field: -; take DeviceAddress and LowSpeedDevice from the parent pipe, -; take Endpoint and MaximumLength fields from API arguments, -; set PID depending on pipe type and provided pipe direction, -; set DataToggle to zero. - mov eax, [ecx+uhci_pipe.Token-sizeof.uhci_pipe] - and eax, 0x107F00 ; keep DeviceAddress and LowSpeedDevice - mov edx, [.endpoint] - and edx, 15 - shl edx, 15 - or eax, edx - mov edx, [.maxpacket] - dec edx - shl edx, 21 - or eax, edx - mov al, USB_PID_SETUP - cmp [.type], CONTROL_PIPE - jz @f - mov al, USB_PID_OUT - test byte [.endpoint], 80h - jz @f - mov al, USB_PID_IN -@@: - mov [edi+uhci_pipe.Token-sizeof.uhci_pipe], eax - bt eax, 20 - setc [.speed] -; 4. Initialize the first TD: -; copy Token from uhci_pipe.Token zeroing reserved bit 20, -; set ControlStatus for future transfers, bit make it inactive, -; set bit 0 in NextTD = "no next TD", -; zero OrigBufferInfo. - pop edx ; restore pointer saved in step 2 - mov [edx+uhci_gtd.Token-sizeof.uhci_gtd], eax - and byte [edx+uhci_gtd.Token+2-sizeof.uhci_gtd], not (1 shl (20-16)) - and eax, 1 shl 20 - shl eax, 6 - or eax, UHCI_INVALID_LENGTH + (3 shl 27) - ; not processed, inactive, allow 3 errors - and [edx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 0 - mov [edx+uhci_gtd.ControlStatus-sizeof.uhci_gtd], eax - mov [edx+uhci_gtd.NextTD-sizeof.uhci_gtd], 1 -; 5. Select the corresponding list and insert to the list. -; 5a. Use Control list for control pipes, Bulk list for bulk pipes. - lea edx, [esi+uhci_controller.ControlED.SoftwarePart-sizeof.uhci_controller] - cmp [.type], BULK_PIPE - jb .insert ; control pipe - lea edx, [esi+uhci_controller.BulkED.SoftwarePart-sizeof.uhci_controller] - jz .insert ; bulk pipe -.interrupt_pipe: -; 5b. For interrupt pipes, let the scheduler select the appropriate list -; based on the current bandwidth distribution and the requested bandwidth. -; This could fail if the requested bandwidth is not available; -; if so, return an error. - lea edx, [esi + uhci_controller.IntEDs - sizeof.uhci_controller] - lea eax, [esi + uhci_controller.IntEDs + 32*sizeof.uhci_static_ep - sizeof.uhci_controller] - movi ecx, 64 - call usb1_select_interrupt_list - test edx, edx - jz .return0 -.insert: -; Insert to the head of the corresponding list. -; Note: inserting to the head guarantees that the list traverse in -; uhci_process_updated_schedule, once started, will not interact with new pipes. -; However, we still need to ensure that links in the new pipe (edi.NextVirt) -; are initialized before links to the new pipe (edx.NextVirt). -; 5c. Insert in the list of virtual addresses. - mov ecx, [edx+usb_pipe.NextVirt] - mov [edi+usb_pipe.NextVirt], ecx - mov [edi+usb_pipe.PrevVirt], edx - mov [ecx+usb_pipe.PrevVirt], edi - mov [edx+usb_pipe.NextVirt], edi -; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, -; store the physical address of the new pipe to previous NextQH. - mov ecx, [edx+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 - inc eax - inc eax - mov [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart], eax -; 6. Return with nonzero eax. - ret -.return0: - xor eax, eax - ret -endp - -; This procedure is called when a pipe is closing (either due to API call -; or due to disconnect); it unlinks a pipe from the corresponding list. -if uhci_static_ep.SoftwarePart <> sizeof.uhci_pipe -.err uhci_unlink_pipe assumes that uhci_static_ep.SoftwarePart == sizeof.uhci_pipe -end if -proc uhci_unlink_pipe - cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE - jnz @f - mov eax, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] - cmp al, USB_PID_IN - setz ch - bt eax, 20 - setc cl - add eax, 1 shl 21 - shr eax, 21 - stdcall usb1_interrupt_list_unlink, eax, ecx -@@: -; Note: we need to ensure that NextVirt field of the pipe is not modified; -; this procedure can be called while uhci_process_updated_schedule processes -; the same pipe, and it needs a correct NextVirt field to continue. - mov edx, [ebx+usb_pipe.NextVirt] - mov eax, [ebx+usb_pipe.PrevVirt] - mov [edx+usb_pipe.PrevVirt], eax - mov [eax+usb_pipe.NextVirt], edx -; Note: eax could be either usb_pipe or usb_static_ep; -; fortunately, NextQH and SoftwarePart have same offsets in both. - mov edx, [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe] - mov [eax+uhci_pipe.NextQH-sizeof.uhci_pipe], edx - 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 -proc uhci_alloc_transfer stdcall uses edi, buffer:dword, size:dword, flags:dword, td:dword, direction:dword -locals -token dd ? -origTD dd ? -packetSize dd ? ; must be the last variable, see usb_init_transfer -endl -; 1. [td] will be the first packet in the transfer. -; Save it to allow unrolling if something will fail. - mov eax, [td] - mov [origTD], eax -; In UHCI one TD describes one packet, transfers should be split into parts -; with size <= endpoint max packet size. -; 2. Get the maximum packet size for endpoint from uhci_pipe.Token -; and generate Token field for TDs. - mov edi, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] - mov eax, edi - shr edi, 21 - inc edi -; zero packet size (it will be set for every packet individually), -; zero reserved bit 20, - and eax, (1 shl 20) - 1 - mov [packetSize], edi -; set the correct PID if it is different from the pipe-wide PID -; (Data and Status stages of control transfers), - mov ecx, [direction] - and ecx, 3 - jz @f - mov al, USB_PID_OUT - dec ecx - jz @f - mov al, USB_PID_IN -@@: -; set the toggle bit for control transfers, - mov ecx, [direction] - test cl, 1 shl 3 - jz @f - and ecx, 1 shl 2 - and eax, not (1 shl 19) - shl ecx, 19-2 - or eax, ecx -@@: -; store the resulting Token in the stack variable. - mov [token], eax -; 3. While the remaining data cannot fit in one packet, -; allocate full packets (of maximal possible size). -.fullpackets: - cmp [size], edi - jbe .lastpacket - call uhci_alloc_packet - test eax, eax - jz .fail - mov [td], eax - add [buffer], edi - sub [size], edi - jmp .fullpackets -.lastpacket: -; 4. The remaining data can fit in one packet; -; allocate the last packet with size = size of remaining data. - mov eax, [size] - mov [packetSize], eax - call uhci_alloc_packet - test eax, eax - jz .fail -; 5. Clear 'short packets are not allowed' bit for the last packet, -; if the caller requested this. -; Note: even if the caller says that short transfers are ok, -; all packets except the last one are marked as 'must be complete': -; if one of them will be short, the software intervention is needed -; to skip remaining packets; uhci_process_finalized_td will handle this -; transparently to the caller. - test [flags], 1 - jz @f - and byte [ecx+uhci_gtd.ControlStatus+3-sizeof.uhci_gtd], not (1 shl (29-24)) - and byte [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], not 1 -@@: -; 6. Update toggle bit in uhci_pipe structure from current value of [token]. - mov edx, [token] - xor edx, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] - and edx, 1 shl 19 - xor [ebx+uhci_pipe.Token-sizeof.uhci_pipe], edx -.nothing: - ret -.fail: - mov edi, uhci_hardware_func - mov eax, [td] - stdcall usb_undo_tds, [origTD] - xor eax, eax - jmp .nothing -endp - -; Helper procedure for uhci_alloc_transfer. Allocates one packet. -proc uhci_alloc_packet -; inherit some variables from the parent uhci_alloc_transfer -virtual at ebp-12 -.token dd ? -.origTD dd ? -.packetSize dd ? - rd 2 -.buffer dd ? -.transferSize dd ? -.Flags dd ? -.td dd ? -.direction dd ? -end virtual -; 1. In UHCI all data for one packet must be on the same page. -; Thus, if the given buffer splits page boundary, we need a temporary buffer -; and code that transfers data between the given buffer and the temporary one. -; 1a. There is no buffer for zero-length packets. - xor eax, eax - cmp [.packetSize], eax - jz .notempbuf -; 1b. A temporary buffer is not required if the first and the last bytes -; of the given buffer are the same except lower 12 bits. - mov edx, [.buffer] - add edx, [.packetSize] - dec edx - xor edx, [.buffer] - test edx, -0x1000 - jz .notempbuf -; 1c. We need a temporary buffer. Allocate [packetSize]*2 bytes, so that -; there must be [packetSize] bytes on one page, -; plus space for a header uhci_original_buffer. - mov eax, [.packetSize] - add eax, eax - add eax, sizeof.uhci_original_buffer - call malloc -; 1d. If failed, return zero. - test eax, eax - jz .nothing -; 1e. Test whether [.packetSize] bytes starting from -; eax + sizeof.uhci_original_buffer are in the same page. -; If so, use eax + sizeof.uhci_original_buffer as a temporary buffer. -; Otherwise, use the beginning of the next page as a temporary buffer -; (since we have overallocated, sufficient space must remain). - lea ecx, [eax+sizeof.uhci_original_buffer] - mov edx, ecx - add edx, [.packetSize] - dec edx - xor edx, ecx - test edx, -0x1000 - jz @f - mov ecx, eax - or ecx, 0xFFF - inc ecx -@@: - mov [eax+uhci_original_buffer.UsedBuffer], ecx - mov ecx, [.buffer] - mov [eax+uhci_original_buffer.OrigBuffer], ecx -; 1f. For SETUP and OUT packets, copy data from the given buffer -; to the temporary buffer now. For IN packets, data go in other direction -; when the transaction completes. - cmp byte [.token], USB_PID_IN - jz .nocopy - push esi edi - mov esi, ecx - mov edi, [eax+uhci_original_buffer.UsedBuffer] - mov ecx, [.packetSize] - mov edx, ecx - shr ecx, 2 - and edx, 3 - rep movsd - mov ecx, edx - rep movsb - pop edi esi -.nocopy: -.notempbuf: -; 2. Allocate the next TD. - push eax - call usb1_allocate_general_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 -; 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, -; generate ControlStatus field, mark as Active -; (for last descriptor, this will be changed by uhci_insert_transfer), -; zero OrigBufferInfo (otherwise uhci_free_td would try to free it). - and [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 0 - mov [eax+uhci_gtd.NextTD-sizeof.uhci_gtd], 1 ; no next TD - mov edx, [ebx+uhci_pipe.Token-sizeof.uhci_pipe] - mov [eax+uhci_gtd.Token-sizeof.uhci_gtd], edx - and byte [eax+uhci_gtd.Token+2-sizeof.uhci_gtd], not (1 shl (20-16)) - and edx, 1 shl 20 - shl edx, 6 - or edx, UHCI_INVALID_LENGTH + (1 shl 23) + (3 shl 27) - ; not processed, active, allow 3 errors - mov [eax+uhci_gtd.ControlStatus-sizeof.uhci_gtd], edx -; 5. Initialize remaining fields of the current TD. -; 5a. Store pointer to the buffer allocated in step 1 (or zero). - pop [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] -; 5b. Store physical address of the next TD. - push eax - sub eax, sizeof.uhci_gtd - call get_phys_addr -; 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 -; it in the last TD - test [ebx+usb_pipe.Type], 1 - jnz @f - cmp ecx, [ebx+usb_pipe.LastTD] - jz @f - or eax, 4 -@@: - mov [ecx+uhci_gtd.NextTD-sizeof.uhci_gtd], eax -; 5c. Store physical address of the buffer: zero if no data present, -; the temporary buffer if it was allocated, the given buffer otherwise. - xor eax, eax - cmp [.packetSize], eax - jz .hasphysbuf - mov eax, [.buffer] - mov edx, [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] - test edx, edx - jz @f - mov eax, [edx+uhci_original_buffer.UsedBuffer] -@@: - call get_phys_addr -.hasphysbuf: - mov [ecx+uhci_gtd.Buffer-sizeof.uhci_gtd], eax -; 5d. For IN transfers, disallow short packets. -; This will be overridden, if needed, by uhci_alloc_transfer. - mov eax, [.token] - mov edx, [.packetSize] - dec edx - cmp al, USB_PID_IN - jnz @f - or byte [ecx+uhci_gtd.ControlStatus+3-sizeof.uhci_gtd], 1 shl (29-24) ; disallow short packets - or byte [ecx+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd], 1 -@@: -; 5e. Get Token field: combine [.token] with [.packetSize]. - shl edx, 21 - or edx, eax - mov [ecx+uhci_gtd.Token-sizeof.uhci_gtd], edx -; 6. Flip toggle bit in [.token]. - xor eax, 1 shl 19 - mov [.token], eax -; 7. Return pointer to the next TD. - pop eax -.nothing: - ret -.fail: - xchg eax, edx - call free - xor eax, eax - ret -endp - -; This procedure is called from the several places in main USB code -; and activates the transfer which was previously allocated by -; uhci_alloc_transfer. -; ecx -> last descriptor for the transfer, ebx -> usb_pipe -proc uhci_insert_transfer -; DEBUGF 1,'K : uhci_insert_transfer: eax=%x, ecx=%x, [esp+4]=%x\n',eax,ecx,[esp+4] - and byte [eax+uhci_gtd.ControlStatus+2-sizeof.uhci_gtd], not (1 shl (23-16)) ; clear Active bit - or byte [ecx+uhci_gtd.ControlStatus+3-sizeof.uhci_gtd], 1 shl (24-24) ; set InterruptOnComplete bit - mov eax, [esp+4] - or byte [eax+uhci_gtd.ControlStatus+2-sizeof.uhci_gtd], 1 shl (23-16) ; set Active bit - test [ebx+usb_pipe.Type], 1 - jnz @f - or byte [eax+uhci_gtd.NextTD-sizeof.uhci_gtd], 4 ; set Depth bit -@@: - ret -endp - -; Free all memory associated with one TD. -; For UHCI, this includes memory for uhci_gtd itself -; and the temporary buffer, if present. -proc uhci_free_td - mov eax, [esp+4] - mov eax, [eax+uhci_gtd.OrigBufferInfo-sizeof.uhci_gtd] - and eax, not 1 - jz .nobuf - push ebx - call free - pop ebx -.nobuf: - sub dword [esp+4], sizeof.uhci_gtd - jmp usb_free_common -endp diff --git a/kernel/branches/Kolibri-acpi/const.inc b/kernel/branches/Kolibri-acpi/const.inc index a6607227df..3664e63c5c 100644 --- a/kernel/branches/Kolibri-acpi/const.inc +++ b/kernel/branches/Kolibri-acpi/const.inc @@ -514,6 +514,8 @@ struct PCIDEV devfn db ? bus db ? irq_line db ? + rb 1 + owner dd ? ; pointer to SRV or 0 ends ; The following macro assume that we are on uniprocessor machine. diff --git a/kernel/branches/Kolibri-acpi/core/dll.inc b/kernel/branches/Kolibri-acpi/core/dll.inc index 442c2a7c22..d9e150719c 100644 --- a/kernel/branches/Kolibri-acpi/core/dll.inc +++ b/kernel/branches/Kolibri-acpi/core/dll.inc @@ -128,10 +128,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 @@ -322,7 +347,7 @@ endp ; allocate kernel memory and loads the specified file ; ; param -; file_name= full path to file +; file_name= path to file ; ; retval ; eax= file image in kernel memory diff --git a/kernel/branches/Kolibri-acpi/core/exports.inc b/kernel/branches/Kolibri-acpi/core/exports.inc index d6825558f5..ff1efadacc 100644 --- a/kernel/branches/Kolibri-acpi/core/exports.inc +++ b/kernel/branches/Kolibri-acpi/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', \ \ @@ -112,6 +113,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/branches/Kolibri-acpi/core/heap.inc b/kernel/branches/Kolibri-acpi/core/heap.inc index 6609378607..5e2a897fe1 100644 --- a/kernel/branches/Kolibri-acpi/core/heap.inc +++ b/kernel/branches/Kolibri-acpi/core/heap.inc @@ -284,8 +284,7 @@ proc alloc_kernel_space stdcall, size:dword cmp eax, [heap_free] ja .error - mov ecx, heap_mutex - call mutex_lock + spin_lock_irqsave heap_mutex mov eax, [size] @@ -345,8 +344,7 @@ proc alloc_kernel_space stdcall, size:dword call md.add_to_used - mov ecx, heap_mutex - call mutex_unlock + spin_unlock_irqrestore heap_mutex mov eax, [esi+block_base] pop edi pop esi @@ -364,8 +362,7 @@ proc alloc_kernel_space stdcall, size:dword jmp .add_used .error_unlock: - mov ecx, heap_mutex - call mutex_unlock + spin_unlock_irqrestore heap_mutex .error: xor eax, eax pop edi @@ -377,8 +374,7 @@ endp align 4 proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword - mov ecx, heap_mutex - call mutex_lock + spin_lock_irqsave heap_mutex mov eax, [base] @@ -446,8 +442,7 @@ proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword lea edx, [mem_block_list+eax*8] list_add edi, edx .m_eq: - mov ecx, heap_mutex - call mutex_unlock + spin_unlock_irqrestore heap_mutex xor eax, eax not eax ret @@ -459,8 +454,7 @@ proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword jmp .add_block .fail: - mov ecx, heap_mutex - call mutex_unlock + spin_unlock_irqrestore heap_mutex xor eax, eax ret endp @@ -544,17 +538,15 @@ proc kernel_free stdcall, base:dword push ebx esi - mov ecx, heap_mutex - call mutex_lock + spin_lock_irqsave heap_mutex mov eax, [base] call md.find_used - mov ecx, heap_mutex cmp [esi+block_flags], USED_BLOCK jne .fail - call mutex_unlock + spin_unlock_irqrestore heap_mutex mov eax, [esi+block_base] mov ecx, [esi+block_size] @@ -564,7 +556,7 @@ proc kernel_free stdcall, base:dword pop esi ebx ret .fail: - call mutex_unlock + spin_unlock_irqrestore heap_mutex xor eax, eax pop esi ebx ret diff --git a/kernel/branches/Kolibri-acpi/core/memory.inc b/kernel/branches/Kolibri-acpi/core/memory.inc index b2f50daaa2..41159cc1e4 100644 --- a/kernel/branches/Kolibri-acpi/core/memory.inc +++ b/kernel/branches/Kolibri-acpi/core/memory.inc @@ -1223,22 +1223,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: @@ -1318,26 +1303,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 @@ -1385,9 +1376,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/branches/Kolibri-acpi/core/peload.inc b/kernel/branches/Kolibri-acpi/core/peload.inc index 444e0dc70e..f447d7a346 100644 --- a/kernel/branches/Kolibri-acpi/core/peload.inc +++ b/kernel/branches/Kolibri-acpi/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 @@ -111,15 +110,17 @@ L2: mov edi, DWORD PTR [esp+32] cmp DWORD PTR [edi+164], 0 je L9 + pushd [edi+164] mov esi, ebp mov ecx, ebp sub esi, DWORD PTR [edi+52] add ecx, DWORD PTR [edi+160] mov eax, esi shr eax, 16 - mov DWORD PTR [esp+12], eax - jmp L11 + mov DWORD PTR [esp+16], eax L12: + mov eax, [ecx+4] + sub [esp], eax lea ebx, [eax-8] xor edi, edi shr ebx, 1 @@ -136,7 +137,7 @@ L14: je L18 dec ax jne L15 - mov eax, DWORD PTR [esp+12] + mov eax, DWORD PTR [esp+16] add WORD PTR [edx+ebp], ax L17: add WORD PTR [edx+ebp], si @@ -149,9 +150,9 @@ L13: jne L14 add ecx, DWORD PTR [ecx+4] L11: - mov eax, DWORD PTR [ecx+4] - test eax, eax - jne L12 + cmp dword [esp], 0 + jg L12 + pop eax L9: mov edx, DWORD PTR [esp+32] cmp DWORD PTR [edx+132], 0 @@ -178,6 +179,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 diff --git a/kernel/branches/Kolibri-acpi/core/sys32.inc b/kernel/branches/Kolibri-acpi/core/sys32.inc index 759798b7c8..5da9fcf9e9 100644 --- a/kernel/branches/Kolibri-acpi/core/sys32.inc +++ b/kernel/branches/Kolibri-acpi/core/sys32.inc @@ -386,7 +386,18 @@ sys_resize_app_memory: ; cmp eax,1 dec ebx jnz .no_application_mem_resize + + mov eax, [pg_data.pages_free] + shl eax, 12 + cmp eax, ecx + jae @f + + xor eax, eax + inc eax + jmp .store_result +@@: stdcall new_mem_resize, ecx +.store_result: mov [esp+32], eax .no_application_mem_resize: ret diff --git a/kernel/branches/Kolibri-acpi/core/taskman.inc b/kernel/branches/Kolibri-acpi/core/taskman.inc index a51f194950..1a3333987a 100644 --- a/kernel/branches/Kolibri-acpi/core/taskman.inc +++ b/kernel/branches/Kolibri-acpi/core/taskman.inc @@ -517,6 +517,7 @@ end if add edi, page_tabs .remap: lodsd + and eax, 0xFFFFF000 or eax, ebx; force user level r/w access stosd add edx, 0x1000 @@ -1208,7 +1209,7 @@ proc set_app_params stdcall,slot:dword, params:dword,\ mov [ebx+REG_EAX], eax mov eax, [esi+0x08] ;app_eip - mov [ebx+REG_EIP], eax;app_entry + mov [ebx+REG_EIP], eax ;app_entry mov [ebx+REG_CS], dword app_code mov ecx, USER_PRIORITY diff --git a/kernel/branches/Kolibri-acpi/detect/vortex86.inc b/kernel/branches/Kolibri-acpi/detect/vortex86.inc index a94ae96313..2fca0a6d1b 100644 --- a/kernel/branches/Kolibri-acpi/detect/vortex86.inc +++ b/kernel/branches/Kolibri-acpi/detect/vortex86.inc @@ -3,15 +3,18 @@ ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; -;; 20/11/2013 yogev_ezra: Initial version ;; -;; Thanks for help to: dunkaist, eAndrew, hidnplayr, Mario ;; -;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -$Revision: 4261 $ +; 20/11/2013 yogev_ezra: Initial version (Vortex86 SoC type detection) +; 26/11/2013 yogev_ezra: Added CPU speed modifier and MMX support flag detection +; Thanks for help to: dunkaist, eAndrew, hidnplayr, Mario + +$Revision: 4310 $ VORTEX86DEBUG = 0 ; For testing in emulators and in non-Vortex86 CPU computers, set this to 1 VORTEX86DEBUGVALUE = 0x35504d44 ; FAKE port output = used for testing +NORTH_BRIDGE = 0x80000000 ; Base address of Vortex86 PCI North Bridge +SOUTH_BRIDGE = 0x80003800 ; Base address of Vortex86 PCI South Bridge ; Detect Vortex86 CPU and generate CPU name in string format (PCI address at 93H~90H in Vortex86 North Bridge contains SoC type) ; Available Vortex86 CPU codes taken from Coreboot project. New codes should be added to "Vortex86SoClist" below @@ -29,11 +32,11 @@ Vortex86SoCname db 'Vortex86 ',0 ; This variable will hold the full name Vortex86SoClist: ; List of Vortex86 CPUs known today. Add new record to this list when new CPU becomes available db 0x31, 'SX ' ; id=1 db 0x32, 'DX ' ; id=2 - db 0x33, 'MX ' ; id=3 + db 0x33, 'MX ' ; id=3 MMX is available starting from CPU code 'MX' (id=3) db 0x34, 'DX2' ; id=4 db 0x35, 'MX+' ; id=5 - db 0x37, 'EX ' ; id=6 -Vortex86SoCnum = ($ - Vortex86SoClist) / 4 ; Calculate the total number of known Vortex86 CPUs (if id=Vortex86SoCnum+1 --> unknown SoC) + db 0x37, 'EX ' ; id=7 +Vortex86SoCnum = ($ - Vortex86SoClist) / 4 ; Calculate the total number of known Vortex86 CPUs endg ; When in debug mode, perform SoC detection regardless of the actual CPU vendor (even for vendors other than DMP) @@ -41,57 +44,115 @@ endg if ~ VORTEX86DEBUG cmp [cpu_vendor], 'Vort' jnz .Vortex86end ; If the CPU vendor is not 'Vortex86 SoC', skip the SoC detection -end if +end if - mov dx, 0xcf8 ; CF8h = Vortex86 PCI Configuration Address port - mov eax, 0x80000090 ; 0x80000090 = Starting PCI address to read from (32-bit register - accessed as DWORD) - out dx, eax ; Send request to PCI address port to retrieve data from this address - mov dx, 0xcfc ; CFCh = Vortex86 PCI Configuration Data port - in eax, dx ; Read data (SoC type) from PCI data port + mov eax, NORTH_BRIDGE+0x90 ; 0x80000090 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get the CPU code from Vortex86 SoC North Bridge PCI register (Register Offset: 93H~90H) if VORTEX86DEBUG ; When in debug mode, pretend that we received port output equal to "VORTEX86DEBUGVALUE" mov eax, VORTEX86DEBUGVALUE -end if +end if + + DEBUGF 1, "K : Vortex86 SoC type register (93H~90H) returned 0x" + test eax, eax ; Check whether the port output was '\0' + jz .nullPCIoutput ; In case the result is '\0' (NULL), skip further testing and exit + mov [Vortex86CPUcode], eax ; Save HEX CPU code to Vortex86CPUcode (so it can be used later) + DEBUGF 1, "%x (%s): ", eax, Vortex86CPUcode ; Print the CPU code (as HEX and as string) to debug log + + mov ebx, 0x444d5000 ; Apply Vortex86 CPU code mask (all Vortex86 SoC have ID in form of "0xNN504d44") + bswap eax ; Assumed it is Vortex86 SoC, the highest byte identifies the exact CPU, so move it to the lowest byte + mov bl, al ; Copy SoC type to BL since EAX (that includes AL) is used implicitly in "LODSD" command below + cmp eax, ebx ; Now see whether the 3 higher bytes were "0x504d44" (which means it's Vortex86) + jnz .notVortex86 ; If it's not Vortex86 - go say so and exit + + sub al, 0x30 ; Current Vortex86 CPU codes are in the range of 31h-37h, so convert them to integer (1,2,...) + mov [Vortex86CPUid], al ; Save the CPUid (1=Vortex86SX, 2=Vortex86DX, ..., 7=Vortex86EX, ...) - DEBUGF 1, "K : Vortex86 SoC register returned 0x" - test eax, eax ; We need to break out in case the result is '\0' since otherwise we will fail at NULL string - jz .nullPCIoutput - mov [Vortex86CPUcode], eax - DEBUGF 1, "%x (%s): ", eax, Vortex86CPUcode - cmp ax, 4d44h ; Check whether it's Vortex86 family (all Vortex86 SoC have ID in form of "0xNN504d44") - jnz .notVortex86 - shr eax, 16 ; Discard lower word in EAX which is always 4d44h in Vortex86 family - cmp al, 50h ; The 3rd byte is always 50h in Vortex86 SoC (if this is the case, we need just the highest byte) - jnz .notVortex86 - mov bl, ah ; Copy SoC type to BL since EAX (that includes AH) is used implicitly in "LODSD" command below mov esi, Vortex86SoClist ; ESI points to the start of Vortex86SoClist (used implicitly in "LODSD" command below) xor ecx, ecx ; Zero ECX (it is used as counter) cld ; Clears the DF flag in the EFLAGS register (DF=0 --> String operations increment ESI) @@: + inc ecx ; Increment our counter cmp ecx, Vortex86SoCnum ; Check if we iterated Vortex86SoCnum times already (i.e. went over the entire Vortex86SoClist) ja .unknownVortex86 ; If the entire list was tested and our CPU is not in that list, it is unknown Vortex86 SoC - inc ecx ; Increment our counter lodsd ; Load DWORD at address DS:ESI into EAX (puts 1 line from Vortex86SoClist into EAX, then increments ESI) cmp bl, al ; Check if our CPU matches the current record in the list jne @b ; No match --> repeat with next record - + shr eax, 8 ; Match found --> drop the SoC type code from Vortex86SoClist name and replace it with \0 mov dword [Vortex86SoCname+8], eax ; Concatenate it with prefix to receive complete SoC name (\0 is string termination) - mov [Vortex86CPUid], cl ; Save the CPUid (1=Vortex86SX, 2=Vortex86DX, ..., Vortex86SoCnum+1=Unknown Vortex86) - - DEBUGF 1, "%s (id=%d)\n", Vortex86SoCname, [Vortex86CPUid]:1 - jmp .Vortex86end ; Say what we have found (CPU name and id) and exit -.nullPCIoutput: ; Emulators and non-Vortex86 CPU computers will usually return \0 in this register - DEBUGF 1, "0 (NULL)\n" - jmp .Vortex86end - -.unknownVortex86: - mov [Vortex86CPUid], cl ; Save the CPUid (Vortex86SoCnum+1=Unknown Vortex86) - DEBUGF 1, "unknown Vortex86 CPU (id=%d, last known is %d)\n", [Vortex86CPUid]:1, Vortex86SoCnum - jmp .Vortex86end - + DEBUGF 1, "%s (id=%d)\n", Vortex86SoCname, [Vortex86CPUid]:1 ; Say what we have found (CPU name and id) + jmp .Vortex86 + .notVortex86: ; In case this register is used by other CPUs for other purpose, it's interesting what it contains DEBUGF 1, "not a Vortex86 CPU\n" + jmp .Vortex86end + +.unknownVortex86: ; It is Vortex86 CPU, but it's not in the list above + DEBUGF 1, "unknown Vortex86 CPU (id=%d)\n", [Vortex86CPUid]:1 ; Inform the user that the CPU is Vortex86 but name is unknown + +.Vortex86: + mov eax, NORTH_BRIDGE+0x60 ; 0x80000060 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get current flags of Vortex86SoC North Bridge STRAP Register (Register Offset: 63h~60h) + DEBUGF 1, "K : Vortex86 STRAP Register (63h~60h) returned 0x%x\n",eax + + mov eax, SOUTH_BRIDGE+0xC0 ; 0x800038C0 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Flags of Vortex86 South Bridge Internal Peripheral Feature Control Register (Register Offset: C3h~C0h) + DEBUGF 1, "K : Vortex86 Internal Peripheral Feature Control Register (C3h~C0h) returned 0x%x\n",eax + + mov eax, SOUTH_BRIDGE+0xCC ; 0x800038CC = PCI Configuration Address Register to read from (8-bit register - accessed as BYTE) + call .Vortex86PCIreg ; Flags of Vortex86 South Bridge Internal Peripheral Feature Control Register III (Register Offset: CCh) + DEBUGF 1, "K : Vortex86 Internal Peripheral Feature Control Register III (CCh) returned 0x%x\n",al + + mov eax, NORTH_BRIDGE+0xA0 ; 0x800000A0 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get current flags of Vortex86SoC North Bridge Host Control Register (Register Offset: A3h~A0h) + DEBUGF 1, "K : Vortex86 Host Control Register (A3h~A0h) returned 0x%x: CPU speed is ",eax + mov bl, al ; The lower byte of Vortex86 Host Control Register contains CPU speed modifier and MMX support status + mov bh, al ; Backup the current AL value, so later we can test whether the value has changed + and bl, 00000111b ; CPU speed modifier is stored in bits 0-2. Value=0 means MAX speed, other values - speed reduction + jz .Vortex86CPUspeedMAX ; 0s in bits 0-2: CPU is at MAX speed (no need to modify) + inc ebx ; The actual value is 1 less than 'Divide by' setting (value '001' means 'Divide by 2', etc.) + DEBUGF 1, "reduced (divide by %d).\nK : Vortex86 changing CPU speed to ", bl ; Print the current CPU speed modifier to the log + and al, 11111000b ; At least one of the bits 0-2 contains 1: CPU is at reduced speed. Set bits 0-2 to 0s to change to MAX +.Vortex86CPUspeedMAX: + DEBUGF 1, "MAX\n" ; Now the CPU should be running at MAX speed (don't write the value to PCI port yet) + + cmp [Vortex86CPUid], 3 ; MMX is available starting from CPU code 'MX' (id=3) + jb .skipVortex86MMX ; No MMX support - skip MMX support status detection (for id=1,2) + DEBUGF 1, "K : Vortex86 MMX support status: MMX is " ; Bits 5-6 in Host Control Register contain MMX status + test al, 100000b ; On MMX-capable Vortex86 SoC, Bit5 = is MMX enabled? (1=Yes/0=No) + jnz .Vortex86MMXenabled ; MMX is already enabled (Bit5=1) + DEBUGF 1, "DISABLED - enabling it for this session\n" ; Print to the log that MMX is disabled + or al, 100000b ; Enable MMX support (don't write the value to PCI port yet) + jmp .AfterMMXenabled +.Vortex86MMXenabled: + DEBUGF 1, "ENABLED\n" ; Print to the log that MMX is enabled +.AfterMMXenabled: + DEBUGF 1, "K : Vortex86 MMX report to CPUID: " ; Print to the log what CPUID command knowns about MMX support + test al, 1000000b ; On MMX-capable Vortex86 SoC, Bit6 = report MMX support to CPUID? (1=Yes/0=No) + jnz .Vortex86MMXreported ; MMX is already reported to CPUID (Bit6=1) + DEBUGF 1, "OFF - turning it ON for this session\n" ; Print to the log that MMX will now be reported to CPUID + or al, 1000000b ; Turn on MMX reporting to CPUID (don't write the value to PCI port yet) + jmp .skipVortex86MMX +.Vortex86MMXreported: + DEBUGF 1, "ON\n" ; Print to the log that MMX reporting to CPUID is enabled + +.skipVortex86MMX: + cmp bh, al ; Check whether AL has changed before (if it did, we need to write it back to PCI port) + jz .Vortex86end ; No change - no need to write to the port + out dx, al ; Write the changed data to PCI port + DEBUGF 1, "K : Vortex86 Host Control Register (A3h~A0h) new value is 0x%x\n",eax + jmp .Vortex86end + +.Vortex86PCIreg: ; Procedure receives input register value in EAX, and returns the output value also in EAX + mov dx, 0xcf8 ; CF8h = Vortex86 PCI Configuration Address port + out dx, eax ; Send request to PCI address port to retrieve data from this address + mov dl, 0xfc ; CFCh = Vortex86 PCI Configuration Data port + in eax, dx ; Read data from PCI data port + ret + +.nullPCIoutput: ; Emulators and non-Vortex86 CPU computers will usually return \0 in this register + DEBUGF 1, "0 (NULL)\n" .Vortex86end: \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/drivers/usbstor.asm b/kernel/branches/Kolibri-acpi/drivers/usbstor.asm index f9d3ec3529..b84b1f15f5 100644 --- a/kernel/branches/Kolibri-acpi/drivers/usbstor.asm +++ b/kernel/branches/Kolibri-acpi/drivers/usbstor.asm @@ -458,6 +458,23 @@ if DUMP_PACKETS DEBUGF 1,'\n' end if stdcall USBNormalTransferAsync, [esi+usb_device_data.OutPipe], edx, command_block_wrapper.sizeof, request_callback1, esi, 0 + test eax, eax + jz .nothing +; 5. If the next stage is data stage in the same direction, enqueue it here. + cmp [esi+usb_device_data.Command.Flags], 0 + js .nothing + cmp [esi+usb_device_data.Command.Length], 0 + jz .nothing + mov edx, [esi+usb_device_data.RequestsQueue+request_queue_item.Next] +if DUMP_PACKETS + DEBUGF 1,'K : USBSTOR out:' + mov eax, [edx+request_queue_item.Buffer] + mov ecx, [esi+usb_device_data.Command.Length] + call debug_dump + DEBUGF 1,'\n' +end if + stdcall USBNormalTransferAsync, [esi+usb_device_data.OutPipe], [edx+request_queue_item.Buffer], [esi+usb_device_data.Command.Length], request_callback2, esi, 0 +.nothing: ret endp @@ -526,28 +543,21 @@ end virtual ; 3. Increment the stage. mov edx, [ecx+usb_device_data.RequestsQueue+request_queue_item.Next] inc [edx+request_queue_item.Stage] -; 4. If there is no data, skip this stage. +; 4. Check whether we need to send the data. +; 4a. If there is no data, skip this stage. cmp [ecx+usb_device_data.Command.Length], 0 jz ..request_get_status -; 5. Initiate USB transfer. If this fails, go to the error handler. - mov eax, [ecx+usb_device_data.InPipe] +; 4b. If data were enqueued in the first stage, do nothing, wait for request_callback2. cmp [ecx+usb_device_data.Command.Flags], 0 - js @f - mov eax, [ecx+usb_device_data.OutPipe] -if DUMP_PACKETS - DEBUGF 1,'K : USBSTOR out:' - push eax ecx - mov eax, [edx+request_queue_item.Buffer] - mov ecx, [ecx+usb_device_data.Command.Length] - call debug_dump - pop ecx eax - DEBUGF 1,'\n' -end if -@@: - stdcall USBNormalTransferAsync, eax, [edx+request_queue_item.Buffer], [ecx+usb_device_data.Command.Length], request_callback2, ecx, 0 + jns .nothing +; 5. Initiate USB transfer. If this fails, go to the error handler. + stdcall USBNormalTransferAsync, [ecx+usb_device_data.InPipe], [edx+request_queue_item.Buffer], [ecx+usb_device_data.Command.Length], request_callback2, ecx, 0 test eax, eax jz .error -; 6. Return. +; 6. The status stage goes to the same direction, enqueue it now. + mov ecx, [.calldata] + jmp ..enqueue_status +.nothing: ret 20 .error: ; Error. @@ -596,15 +606,20 @@ end if test eax, eax jnz .error ; No error. +; If the previous stage was in same direction, do nothing; status request is already enqueued. + cmp [ecx+usb_device_data.Command.Flags], 0 + js .nothing ..request_get_status: ; 3. Increment the stage. mov edx, [ecx+usb_device_data.RequestsQueue+request_queue_item.Next] inc [edx+request_queue_item.Stage] ; 4. Initiate USB transfer. If this fails, go to the error handler. +..enqueue_status: lea edx, [ecx+usb_device_data.Status] stdcall USBNormalTransferAsync, [ecx+usb_device_data.InPipe], edx, command_status_wrapper.sizeof, request_callback3, ecx, 0 test eax, eax jz .error +.nothing: ret 20 .error: ; Error. diff --git a/kernel/branches/Kolibri-acpi/gui/window.inc b/kernel/branches/Kolibri-acpi/gui/window.inc index ff74dfa489..a71cd0d4f3 100644 --- a/kernel/branches/Kolibri-acpi/gui/window.inc +++ b/kernel/branches/Kolibri-acpi/gui/window.inc @@ -1884,7 +1884,6 @@ align 4 ; get WinMap start push esi mov esi, [_display.width] -; imul edi, ebx mov edi, [d_width_calc_area + ebx*4] add edi, eax diff --git a/kernel/branches/Kolibri-acpi/init.inc b/kernel/branches/Kolibri-acpi/init.inc index e2d0798482..466503348c 100644 --- a/kernel/branches/Kolibri-acpi/init.inc +++ b/kernel/branches/Kolibri-acpi/init.inc @@ -41,6 +41,8 @@ proc mem_test mov [BOOT_VARS-OS_BASE + 0x9108], eax mov [BOOT_VARS-OS_BASE + 0x910C], edi mov [BOOT_VARS-OS_BASE + 0x9110], eax + inc eax + mov [BOOT_VARS-OS_BASE + 0x9114], eax .ret: ret endp diff --git a/kernel/branches/Kolibri-acpi/kernel.asm b/kernel/branches/Kolibri-acpi/kernel.asm index fbf0cfdf4a..2fa35afbe4 100644 --- a/kernel/branches/Kolibri-acpi/kernel.asm +++ b/kernel/branches/Kolibri-acpi/kernel.asm @@ -2540,10 +2540,10 @@ sysfn_mouse_acceleration: ; 18.19 = set/get mouse features ; cmp ecx,4 ; set mouse pointer position dec ecx jnz .set_mouse_button - cmp dx, word[_display.width] + cmp dx, word[_display.height] jae .end rol edx, 16 - cmp dx, word[_display.height] + cmp dx, word[_display.width] jae .end mov [MOUSE_X], edx call wakeup_osloop diff --git a/kernel/branches/Kolibri-acpi/kernel32.inc b/kernel/branches/Kolibri-acpi/kernel32.inc index 220f4299f6..141766885d 100644 --- a/kernel/branches/Kolibri-acpi/kernel32.inc +++ b/kernel/branches/Kolibri-acpi/kernel32.inc @@ -237,6 +237,8 @@ include "blkdev/ide_cache.inc" ; HD drive controller include "blkdev/hd_drv.inc" +; Access through BIOS +include "blkdev/bd_drv.inc" ; CD drive controller diff --git a/kernel/branches/Kolibri-acpi/network/IPv4.inc b/kernel/branches/Kolibri-acpi/network/IPv4.inc index a0ba259c47..b3c582486d 100644 --- a/kernel/branches/Kolibri-acpi/network/IPv4.inc +++ b/kernel/branches/Kolibri-acpi/network/IPv4.inc @@ -19,6 +19,13 @@ $Revision: 3515 $ IPv4_MAX_FRAGMENTS = 64 +IPv4_MAX_ROUTES = 64 + +IPv4_ROUTE_FLAG_UP = 1 shl 0 +IPv4_ROUTE_FLAG_GATEWAY = 1 shl 1 +IPv4_ROUTE_FLAG_HOST = 1 shl 2 +IPv4_ROUTE_FLAG_D = 1 shl 3 ; Route was created by a redirect +IPv4_ROUTE_FLAG_M = 1 shl 4 ; Route was modified by a redirect struct IPv4_header @@ -54,6 +61,16 @@ struct IPv4_FRAGMENT_entry ; This structure will replace the ethern ; Ip header begins here (we will need the IP header to re-construct the complete packet) ends +struct IPv4_ROUTE + + Destination dd ? + Gateway dd ? + Flags dd ? + Use dd ? + Interface dd ? + +ends + uglobal align 4 @@ -70,6 +87,8 @@ align 4 IPv4_FRAGMENT_LIST rb IPv4_MAX_FRAGMENTS * sizeof.IPv4_FRAGMENT_slot + IPv4_ROUTES rd IPv4_MAX_ROUTES * sizeof.IPv4_ROUTE + endg diff --git a/kernel/branches/Kolibri-acpi/network/icmp.inc b/kernel/branches/Kolibri-acpi/network/icmp.inc index b30b3f1d80..165f45b26c 100644 --- a/kernel/branches/Kolibri-acpi/network/icmp.inc +++ b/kernel/branches/Kolibri-acpi/network/icmp.inc @@ -230,7 +230,7 @@ ICMP_input: test eax, eax jnz @f call NET_ptr_to_num4 - inc [UDP_PACKETS_TX + edi] + inc [ICMP_PACKETS_TX + edi] @@: ret diff --git a/kernel/branches/Kolibri-acpi/network/loopback.inc b/kernel/branches/Kolibri-acpi/network/loopback.inc index 38835432d6..72eeb76db0 100644 --- a/kernel/branches/Kolibri-acpi/network/loopback.inc +++ b/kernel/branches/Kolibri-acpi/network/loopback.inc @@ -35,7 +35,7 @@ LOOPBACK_DEVICE: .packets_rx dd 0 .link_state dd -1 - .hwacc dd 0 + .hwacc dd NET_HWACC_TCP_IPv4_IN + NET_HWACC_TCP_IPv4_OUT .namestr db 'loopback', 0 diff --git a/kernel/branches/Kolibri-acpi/network/socket.inc b/kernel/branches/Kolibri-acpi/network/socket.inc index e154149126..12ddd6e876 100644 --- a/kernel/branches/Kolibri-acpi/network/socket.inc +++ b/kernel/branches/Kolibri-acpi/network/socket.inc @@ -28,7 +28,7 @@ struct SOCKET PID dd ? ; process ID TID dd ? ; thread ID Domain dd ? ; INET/LOCAL/.. - Type dd ? ; RAW/STREAM/DGRAP + Type dd ? ; RAW/STREAM/DGRAM Protocol dd ? ; ICMP/IPv4/ARP/TCP/UDP errorcode dd ? device dd ? ; driver pointer, socket pointer if it's an LOCAL socket @@ -92,8 +92,8 @@ struct TCP_SOCKET IP_SOCKET SND_MAX dd ? ; congestion control - SND_CWND dd ? - SND_SSTHRESH dd ? + SND_CWND dd ? ; congestion window + SND_SSTHRESH dd ? ; slow start threshold ;---------------------- ; Transmit timing stuff @@ -141,10 +141,6 @@ struct TCP_SOCKET IP_SOCKET seg_next dd ? ; re-assembly queue - temp_bits db ? - rb 3 ; align - - ends struct UDP_SOCKET IP_SOCKET @@ -715,12 +711,13 @@ SOCKET_close: ret .tcp: - cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED ; state must be LISTEN, SYN_SENT or CLOSED - jb .free call TCP_usrclosed - call TCP_output ;;;; Fixme: is this nescessary?? - call SOCKET_free + + test eax, eax + jz @f + call TCP_output ; If connection is not closed yet, send the FIN + @@: ret @@ -1562,10 +1559,10 @@ SOCKET_ring_write: pop ecx ; unlock mutex - push eax ecx + pusha lea ecx, [eax + RING_BUFFER.mutex] call mutex_unlock ; TODO: check what registers this function actually destroys - pop ecx eax + popa ret @@ -2085,7 +2082,6 @@ SOCKET_num_to_ptr: mov eax, [eax + SOCKET.NextPtr] or eax, eax jz .error - diff16 "tetten", 0, $ cmp [eax + SOCKET.Number], ecx jne .next_socket @@ -2205,7 +2201,7 @@ SOCKET_check_owner: ; ; Kernel calls this function when a certain process ends ; This function will check if the process had any open sockets -; And update them accordingly +; And update them accordingly (clean up) ; ; IN: edx = pid ; OUT: / @@ -2251,7 +2247,7 @@ SOCKET_process_end: cmp [eax + SOCKET.Protocol], IP_PROTO_TCP jne .free - call TCP_close + call TCP_disconnect jmp .closed .free: @@ -2356,20 +2352,9 @@ SOCKET_is_disconnected: and [eax + SOCKET.state], not (SS_ISCONNECTING + SS_ISCONNECTED + SS_ISDISCONNECTING) or [eax + SOCKET.state], SS_CANTRCVMORE + SS_CANTSENDMORE - cmp [eax + SOCKET.Protocol], IP_PROTO_TCP - je .tcp - - cmp [eax + SOCKET.Protocol], IP_PROTO_UDP - je .udp jmp SOCKET_notify - .tcp: - .udp: - mov [eax + UDP_SOCKET.LocalPort], 0 ; UDP and TCP structs store localport at the same offset - mov [eax + UDP_SOCKET.RemotePort], 0 - - jmp SOCKET_notify ;----------------------------------------------------------------- diff --git a/kernel/branches/Kolibri-acpi/network/stack.inc b/kernel/branches/Kolibri-acpi/network/stack.inc index 64eebc9112..20ca64e73f 100644 --- a/kernel/branches/Kolibri-acpi/network/stack.inc +++ b/kernel/branches/Kolibri-acpi/network/stack.inc @@ -110,7 +110,7 @@ SS_MORETOCOME = 0x4000 SS_BLOCKED = 0x8000 -SOCKET_MAXDATA = 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8 +SOCKET_MAXDATA = 4096*8 ; must be 4096*(power of 2) where 'power of 2' is at least 8 MAX_backlog = 20 ; maximum backlog for stream sockets ; Error Codes @@ -152,7 +152,8 @@ NET_LINK_PPP = 2 ; Point to Point Protocol (PPPoE, ...) NET_LINK_IEEE802.11 = 3 ; IEEE 802.11 (WiFi) ; Hardware acceleration bits -HWACC_TCP_IPv4 = 1 shl 0 +NET_HWACC_TCP_IPv4_IN = 1 shl 0 +NET_HWACC_TCP_IPv4_OUT = 1 shl 1 struct NET_DEVICE diff --git a/kernel/branches/Kolibri-acpi/network/tcp.inc b/kernel/branches/Kolibri-acpi/network/tcp.inc index 5e05e03867..4675e18fc3 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp.inc @@ -143,6 +143,62 @@ align 4 TCP_input_event dd ? endg +uglobal +align 4 + + TCPS_accepts dd ? ; #SYNs received in LISTEN state + TCPS_closed dd ? ; #connections closed (includes drops) + TCPS_connattempt dd ? ; #connections initiated (calls to connect) + TCPS_conndrops dd ? ; #embryonic connections dropped (before SYN received) + TCPS_connects dd ? ; #connections established actively or passively + TCPS_delack dd ? ; #delayed ACKs sent + TCPS_drops dd ? ; #connections dropped (after SYN received) + TCPS_keepdrops dd ? ; #connections dropped in keepalive (established or awaiting SYN) + TCPS_keepprobe dd ? ; #keepalive probes sent + TCPS_keeptimeo dd ? ; #times keepalive timer or connections-establishment timer expire + TCPS_pawsdrop dd ? ; #segments dropped due to PAWS + TCPS_pcbcachemiss dd ? ; #times PCB cache comparison fails + TCPS_persisttimeo dd ? ; #times persist timer expires + TCPS_predack dd ? ; #times header prediction correct for ACKs + TCPS_preddat dd ? ; #times header prediction correct for data packets + TCPS_rcvackbyte dd ? ; #bytes ACKed by received ACKs + TCPS_rcvackpack dd ? ; #received ACK packets + TCPS_rcvacktoomuch dd ? ; #received ACKs for unsent data + TCPS_rcvafterclose dd ? ; #packets received after connection closed + TCPS_rcvbadoff dd ? ; #packets received with invalid header length + TCPS_rcvbadsum dd ? ; #packets received with checksum errors + TCPS_rcvbyte dd ? ; #bytes received in sequence + TCPS_rcvbyteafterwin dd ? ; #bytes received beyond advertised window + TCPS_rcvdupack dd ? ; #duplicate ACKs received + TCPS_rcvdupbyte dd ? ; #bytes receivedin completely duplicate packets + TCPS_rcvduppack dd ? ; #packets received with completely duplicate bytes + TCPS_rcvoobyte dd ? ; #out-of-order bytes received + TCPS_rcvoopack dd ? ; #out-of-order packets received + TCPS_rcvpack dd ? ; #packets received in sequence + TCPS_rcvpackafterwin dd ? ; #packets with some data beyond advertised window + TCPS_rcvpartdupbyte dd ? ; #duplicate bytes in part-duplicate packets + TCPS_rcvpartduppack dd ? ; #packets with some duplicate data + TCPS_rcvshort dd ? ; #packets received too short + TCPS_rcvtotal dd ? ; #total packets received + TCPS_rcvwinprobe dd ? ; #window probe packets received + TCPS_rcvwinupd dd ? ; #received window update packets + TCPS_rexmttimeo dd ? ; #retransmission timeouts + TCPS_rttupdated dd ? ; #times RTT estimators updated + TCPS_segstimed dd ? ; #segments for which TCP tried to measure RTT + TCPS_sndacks dd ? ; #ACK-only packets sent (data length = 0) + TCPS_sndbyte dd ? ; #data bytes sent + TCPS_sndctrl dd ? ; #control (SYN, FIN, RST) packets sent (data length = 0) + TCPS_sndpack dd ? ; #data packets sent (data length > 0) + TCPS_sndprobe dd ? ; #window probes sent (1 byte of data forced by persist timer) + TCPS_sndrexmitbyte dd ? ; #data bytes retransmitted + TCPS_sndrexmitpack dd ? ; #data packets retransmitted + TCPS_sndtotal dd ? ; total #packets sent + TCPS_sndurg dd ? ; #packets sent with URG-only (data length=0) + TCPS_sndwinup dd ? ; #window update-only packets sent (data length=0) + TCPS_timeoutdrop dd ? ; #connections dropped in retransmission timeout + +endg + ;----------------------------------------------------------------- ; diff --git a/kernel/branches/Kolibri-acpi/network/tcp_input.inc b/kernel/branches/Kolibri-acpi/network/tcp_input.inc index 2278517365..d618461912 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp_input.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp_input.inc @@ -78,7 +78,13 @@ TCP_input: align 4 -TCP_process_input: +proc TCP_process_input + +locals + dataoffset dd ? + timestamp dd ? + temp_bits db ? +endl xor esi, esi mov ecx, MANUAL_DESTROY @@ -94,6 +100,7 @@ TCP_process_input: get_from_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .wait push [esi + TCP_queue_entry.timestamp] + pop [timestamp] push [esi + TCP_queue_entry.buffer_ptr] mov ebx, [esi + TCP_queue_entry.device_ptr] @@ -109,8 +116,8 @@ TCP_process_input: je .checksum_ok ; re-calculate the checksum (if not already done by hw) -; test [ebx + NET_DEVICE.hwacc], HWACC_TCP_IPv4_IN -; jnz .checksum_ok + test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_IN + jnz .checksum_ok push ecx esi pushw [esi + TCP_header.Checksum] @@ -123,12 +130,13 @@ TCP_process_input: .checksum_ok: ; Verify the data offset - and [edx + TCP_header.DataOffset], 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header) - shr [edx + TCP_header.DataOffset], 2 - cmp [edx + TCP_header.DataOffset], sizeof.TCP_header ; Now see if it's at least the size of a standard TCP header - jb .drop_no_socket ; If not, drop the packet - movzx eax, [edx + TCP_header.DataOffset] + and al, 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header) + shr al, 2 + cmp al, sizeof.TCP_header ; Now see if it's at least the size of a standard TCP header + jb .drop_no_socket ; If not, drop the packet + mov [dataoffset], eax + sub ecx, eax ; substract TCP header size from total segment size jb .drop_no_socket ; If total segment size is less then the advertised header size, drop packet DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: %u bytes of data\n", ecx @@ -211,7 +219,7 @@ TCP_process_input: ;--------------------------- ; disable all temporary bits - mov [ebx + TCP_SOCKET.temp_bits], 0 + mov [temp_bits], 0 ;--------------------------------------- ; unscale the window into a 32 bit value @@ -245,7 +253,7 @@ TCP_process_input: mov ebx, eax - mov [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET ;;; FIXME: should we take over bits from previous socket? + mov [temp_bits], TCP_BIT_DROPSOCKET push dword [edi + 4] ; Ipv4 destination addres pop [ebx + IP_SOCKET.LocalIP] @@ -267,18 +275,18 @@ TCP_process_input: ;-------------------- ; Process TCP options +;;; FIXME: for LISTEN, options should be called after we determined route, we need it for MSS +;;; cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN ; no options when in listen state +;;; jz .not_uni_xfer ; also no header prediction + push ecx - movzx ecx, [edx + TCP_header.DataOffset] + mov ecx, [dataoffset] cmp ecx, sizeof.TCP_header ; Does header contain any options? je .no_options DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Segment has options\n" -;;; FIXME: for LISTEN, options should be called after we determined route, we need it for MSS -;;; cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN ; no options when in listen state -;;; jz .not_uni_xfer ; also no header prediction - add ecx, edx lea esi, [edx + sizeof.TCP_header] @@ -311,9 +319,10 @@ TCP_process_input: test [edx + TCP_header.Flags], TH_SYN jz @f + xor eax, eax lodsw rol ax, 8 - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Maxseg=%u\n", ax + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Maxseg=%u\n", eax call TCP_mss @@: jmp .opt_loop @@ -366,10 +375,11 @@ TCP_process_input: @@: lodsd + bswap eax mov [ebx + TCP_SOCKET.ts_val], eax lodsd ; timestamp echo reply mov [ebx + TCP_SOCKET.ts_ecr], eax - or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP + or [temp_bits], TCP_BIT_TIMESTAMP ; Since we have a timestamp, lets do the paws test right away! @@ -380,11 +390,11 @@ TCP_process_input: test eax, eax jz .no_paws cmp eax, [ebx + TCP_SOCKET.ts_val] - jge .no_paws + jbe .no_paws DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: PAWS: detected an old segment\n" - mov eax, [esp+4+4] ; tcp_now + mov eax, [timestamp] sub eax, [ebx + TCP_SOCKET.ts_recent_age] pop ecx @@ -474,9 +484,9 @@ TCP_process_input: ; Update RTT estimators - test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP + test [temp_bits], TCP_BIT_TIMESTAMP jz .no_timestamp_rtt - mov eax, [esp + 4] ; timestamp when this segment was received + mov eax, [timestamp] sub eax, [ebx + TCP_SOCKET.ts_ecr] inc eax call TCP_xmit_timer @@ -536,12 +546,11 @@ TCP_process_input: DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction: we are receiving %u bytes\n", ecx - add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied - - movzx esi, [edx + TCP_header.DataOffset] + mov esi, [dataoffset] add esi, edx lea eax, [ebx + STREAM_SOCKET.rcv] call SOCKET_ring_write ; Add the data to the socket buffer + add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied mov eax, ebx call SOCKET_notify @@ -558,12 +567,13 @@ TCP_process_input: DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction failed\n" ; Calculate receive window size - push edx - mov eax, SOCKETBUFFSIZE + mov eax, SOCKET_MAXDATA sub eax, [ebx + STREAM_SOCKET.rcv.size] + DEBUGF DEBUG_NETWORK_VERBOSE, "Space in receive buffer=%d\n", eax mov edx, [ebx + TCP_SOCKET.RCV_ADV] sub edx, [ebx + TCP_SOCKET.RCV_NXT] + DEBUGF DEBUG_NETWORK_VERBOSE, "Current advertised window=%d\n", edx cmp eax, edx jg @f mov eax, edx @@ -583,8 +593,9 @@ TCP_process_input: ;---------------------------- ; trim any data not in window -; check for duplicate data at beginning of segment (635) +; 1. Check for duplicate data at beginning of segment +; Calculate number of bytes we need to drop mov eax, [ebx + TCP_SOCKET.RCV_NXT] sub eax, [edx + TCP_header.SequenceNumber] jle .no_duplicate @@ -609,7 +620,7 @@ TCP_process_input: dec eax .no_dup_syn: -; Check for entire duplicate segment (646) +; 2. Check for entire duplicate segment cmp eax, ecx ; eax holds number of bytes to drop, ecx is data size jb .duplicate jnz @f @@ -623,16 +634,19 @@ TCP_process_input: ; send an ACK and resynchronize and drop any data. ; But keep on processing for RST or ACK - DEBUGF DEBUG_NETWORK_VERBOSE, "616\n" or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW mov eax, ecx -;TODO: update stats + +;;; TODO: update stats ;----------------------------------------------- ; Remove duplicate data and update urgent offset .duplicate: -;;; TODO: 677 + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: trimming duplicate data\n" + +; Trim data from left side of window + add [dataoffset], eax add [edx + TCP_header.SequenceNumber], eax sub ecx, eax @@ -643,10 +657,10 @@ TCP_process_input: @@: ;-------------------------------------------------- -; Handle data that arrives after process terminates (687) +; Handle data that arrives after process terminates .no_duplicate: - cmp [ebx + SOCKET.PID], 0 + cmp [ebx + SOCKET.PID], 0 ;;; TODO: use socket flags instead?? jne .not_terminated cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT jbe .not_terminated @@ -655,11 +669,11 @@ TCP_process_input: mov eax, ebx call TCP_close -;;;TODO: update stats +;;; TODO: update stats jmp .respond_seg_reset ;---------------------------------------- -; Remove data beyond right edge of window (700-736) +; Remove data beyond right edge of window .not_terminated: mov eax, [edx + TCP_header.SequenceNumber] @@ -688,32 +702,29 @@ TCP_process_input: jmp .findpcb ; FIXME: skip code for unscaling window, ... .no_new_request: -; If window is closed can only take segments at window edge, and have to drop data and PUSH from +; If window is closed, we can only take segments at window edge, and have to drop data and PUSH from ; incoming segments. Continue processing, but remember to ACK. Otherwise drop segment and ACK cmp [ebx + TCP_SOCKET.RCV_WND], 0 jne .drop_after_ack - mov eax, [edx + TCP_header.SequenceNumber] - cmp eax, [ebx + TCP_SOCKET.RCV_NXT] + mov esi, [edx + TCP_header.SequenceNumber] + cmp esi, [ebx + TCP_SOCKET.RCV_NXT] jne .drop_after_ack - DEBUGF DEBUG_NETWORK_VERBOSE, "690\n" or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW ;;; TODO: update stats - jmp .no_excess_data .dont_drop_all: ;;; TODO: update stats -;;; TODO: 733 - - sub ecx, eax - and [ebx + TCP_SOCKET.t_flags], not (TH_PUSH or TH_FIN) + DEBUGF DEBUG_NETWORK_VERBOSE, "Trimming %u bytes from the right of the window\n" + sub ecx, eax ; remove data from the right side of window (decrease data length) + and [edx + TCP_header.Flags], not (TH_PUSH or TH_FIN) .no_excess_data: ;----------------- -; Record timestamp (737-746) +; Record timestamp ; If last ACK falls within this segments sequence numbers, record its timestamp - test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP + test [temp_bits], TCP_BIT_TIMESTAMP jz .no_timestamp mov eax, [ebx + TCP_SOCKET.last_ack_sent] sub eax, [edx + TCP_header.SequenceNumber] @@ -727,7 +738,7 @@ TCP_process_input: DEBUGF DEBUG_NETWORK_VERBOSE, "Recording timestamp\n" - mov eax, [esp + 4] ; tcp_now + mov eax, [timestamp] mov [ebx + TCP_SOCKET.ts_recent_age], eax mov eax, [ebx + TCP_SOCKET.ts_val] mov [ebx + TCP_SOCKET.ts_recent], eax @@ -882,7 +893,9 @@ TCP_process_input: mov eax, [ebx + TCP_SOCKET.SND_WND] cmp eax, [ebx + TCP_SOCKET.SND_CWND] - cmova eax, [ebx + TCP_SOCKET.SND_CWND] + jbe @f + mov eax, [ebx + TCP_SOCKET.SND_CWND] + @@: shr eax, 1 push edx xor edx, edx @@ -993,13 +1006,13 @@ TCP_process_input: DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: acceptable ACK for %u bytes\n", edi ;------------------------------------------ -; RTT measurements and retransmission timer (912-926) +; RTT measurements and retransmission timer ; If we have a timestamp, update smoothed RTT - test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP + test [temp_bits], TCP_BIT_TIMESTAMP jz .timestamp_not_present - mov eax, [esp+4] + mov eax, [timestamp] sub eax, [ebx + TCP_SOCKET.ts_ecr] inc eax call TCP_xmit_timer @@ -1028,7 +1041,7 @@ TCP_process_input: cmp eax, [edx + TCP_header.AckNumber] jne .more_data and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission - or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT + or [temp_bits], TCP_BIT_NEEDOUTPUT jmp .no_restart .more_data: test [ebx + TCP_SOCKET.timer_flags], timer_flag_persist @@ -1067,7 +1080,9 @@ TCP_process_input: pop ecx cmp esi, eax - cmova esi, eax + jbe @f + mov esi, eax + @@: mov [ebx + TCP_SOCKET.SND_CWND], esi ;------------------------------------------ @@ -1175,12 +1190,9 @@ TCP_process_input: call mutex_unlock pop ebx - push ebx mov eax, ebx - call TCP_disconnect - pop ebx - - jmp .destroy_new_socket + call TCP_close + jmp .drop_no_socket .ack_tw: mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL @@ -1228,7 +1240,7 @@ align 4 TCP_rcvseqinit ebx mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED - mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval ;;;; macro or [ebx + TCP_SOCKET.timer_flags], timer_flag_keepalive @@ -1238,7 +1250,7 @@ align 4 lea eax, [ebx + STREAM_SOCKET.rcv] call SOCKET_ring_create - and [ebx + TCP_SOCKET.temp_bits], not TCP_BIT_DROPSOCKET + and [temp_bits], not TCP_BIT_DROPSOCKET pusha mov eax, ebx @@ -1355,7 +1367,16 @@ align 4 inc [edx + TCP_header.SequenceNumber] -;;; TODO: Drop any received data that follows receive window (590) +; Drop any received data that doesnt fit in the receive window. + cmp ecx, [ebx + TCP_SOCKET.RCV_WND] + jbe .dont_trim + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: received data does not fit in window, trimming %u bytes\n", eax + mov ecx, [ebx + TCP_SOCKET.RCV_WND] + and [edx + TCP_header.Flags], not (TH_FIN) +;;; TODO: update stats + + .dont_trim: mov eax, [edx + TCP_header.SequenceNumber] mov [ebx + TCP_SOCKET.RCV_UP], eax @@ -1409,7 +1430,7 @@ align 4 push [edx + TCP_header.AckNumber] pop [ebx + TCP_SOCKET.SND_WL2] - or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT + or [temp_bits], TCP_BIT_NEEDOUTPUT .no_window_update: @@ -1475,7 +1496,7 @@ align 4 or [ebx + TCP_SOCKET.t_flags], TF_DELACK pusha - movzx esi, [edx + TCP_header.DataOffset] + mov esi, [dataoffset] add esi, edx lea eax, [ebx + STREAM_SOCKET.rcv] call SOCKET_ring_write ; Add the data to the socket buffer @@ -1489,16 +1510,16 @@ align 4 jmp .data_done .out_of_order: - - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP data is out of order\n" + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP data is out of order!\nSequencenumber is %u, we expected %u.\n", \ + [edx + TCP_header.SequenceNumber], [ebx + TCP_SOCKET.RCV_NXT] ; Uh-oh, some data is out of order, lets call TCP reassemble for help call TCP_reassemble - DEBUGF DEBUG_NETWORK_VERBOSE, "1470\n" +; Generate ACK immediately, to let the other end know that a segment was received out of order, +; and to tell it what sequence number is expected. This aids the fast-retransmit algorithm. or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW - .data_done: ;--------------- @@ -1517,7 +1538,7 @@ align 4 mov eax, ebx call SOCKET_cant_recv_more - mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW inc [ebx + TCP_SOCKET.RCV_NXT] .not_first_fin: @@ -1539,29 +1560,54 @@ align 4 dd .fin_timed ; TCPS_TIMED_WAIT .fin_syn_est: - mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT jmp .final_processing .fin_wait1: - mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSING jmp .final_processing .fin_wait2: - mov [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT mov eax, ebx call TCP_cancel_timers - mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL - or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait call SOCKET_is_disconnected - jmp .final_processing .fin_timed: mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait - jmp .final_processing + +;----------------- +; Final processing + + .final_processing: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Final processing\n" + + push ebx + lea ecx, [ebx + SOCKET.mutex] + call mutex_unlock + pop eax + + test [temp_bits], TCP_BIT_NEEDOUTPUT + jnz .need_output + + test [eax + TCP_SOCKET.t_flags], TF_ACKNOW + jz .dumpit + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK now!\n" + + .need_output: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: need output\n" + call TCP_output + + .dumpit: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: dumping\n" + + call NET_packet_free + jmp .loop + + +;----------------- +; Drop the segment .drop_after_ack: @@ -1598,35 +1644,6 @@ align 4 jnz .respond_syn jmp .dumpit -;----------------- -; Final processing - - .final_processing: - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Final processing\n" - - push ebx - lea ecx, [ebx + SOCKET.mutex] - call mutex_unlock - pop eax - - test [eax + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT - jnz .need_output - - test [eax + TCP_SOCKET.t_flags], TF_ACKNOW - jz .dumpit - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK now!\n" - - .need_output: - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: need output\n" - call TCP_output - - .dumpit: - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: dumping\n" - - call NET_packet_free - add esp, 4 - jmp .loop - ;--------- ; Respond @@ -1687,7 +1704,7 @@ align 4 popa .destroy_new_socket: - test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET + test [temp_bits], TCP_BIT_DROPSOCKET jz .drop_no_socket mov eax, ebx @@ -1697,5 +1714,6 @@ align 4 DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop (no socket)\n" call NET_packet_free - add esp, 4 jmp .loop + +endp diff --git a/kernel/branches/Kolibri-acpi/network/tcp_output.inc b/kernel/branches/Kolibri-acpi/network/tcp_output.inc index 0b3b74555d..037df19995 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp_output.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp_output.inc @@ -21,14 +21,17 @@ $Revision: 3289 $ ; TCP_output ; ; IN: eax = socket pointer -; -; OUT: / +; OUT: eax = 0 on success/errorcode ; ;----------------------------------------------------------------- align 4 -TCP_output: +proc TCP_output - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x\n", eax +locals + temp_bits db ? +endl + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x state=%u\n", eax, [eax + TCP_SOCKET.t_state] push eax lea ecx, [eax + SOCKET.mutex] @@ -57,7 +60,7 @@ TCP_output: .not_idle: .again: - mov [eax + TCP_SOCKET.temp_bits], 0 + mov [temp_bits], 0 mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) sub ebx, [eax + TCP_SOCKET.SND_UNA] ; @@ -145,7 +148,7 @@ TCP_output: jbe @f mov esi, [eax + TCP_SOCKET.t_maxseg] - or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT + or [temp_bits], TCP_BIT_SENDALOT @@: ;-------------------------------------------- @@ -173,31 +176,31 @@ TCP_output: jz .len_zero cmp esi, [eax + TCP_SOCKET.t_maxseg] - je TCP_send + je .send add ebx, esi ; offset + length cmp ebx, [eax + STREAM_SOCKET.snd.size] jb @f test [eax + TCP_SOCKET.t_flags], TF_NODELAY - jnz TCP_send + jnz .send mov ebx, [eax + TCP_SOCKET.SND_MAX] cmp ebx, [eax + TCP_SOCKET.SND_UNA] - je TCP_send + je .send @@: test [eax + TCP_SOCKET.t_force], -1 ;;; - jnz TCP_send + jnz .send mov ebx, [eax + TCP_SOCKET.max_sndwnd] shr ebx, 1 cmp esi, ebx - jae TCP_send + jae .send mov ebx, [eax + TCP_SOCKET.SND_NXT] cmp ebx, [eax + TCP_SOCKET.SND_MAX] - jb TCP_send + jb .send .len_zero: @@ -229,9 +232,10 @@ TCP_output: mov edi, [eax + TCP_SOCKET.t_maxseg] shl edi, 1 -; cmp ebx, edi -; jae TCP_send + cmp ebx, edi + jae .send + shl ebx, 1 ; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark ; jae TCP_send @@ -240,17 +244,15 @@ TCP_output: ;-------------------------- ; Should a segment be sent? (174) - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: 174\n" - test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK - jnz TCP_send + jnz .send test dl, TH_SYN + TH_RST ; we need to send a SYN or RST - jnz TCP_send + jnz .send mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer cmp ebx, [eax + TCP_SOCKET.SND_UNA] - ja TCP_send + ja .send test dl, TH_FIN jz .enter_persist ; no reason to send, enter persist state @@ -258,11 +260,11 @@ TCP_output: ; FIN was set, only send if not already sent, or on retransmit test [eax + TCP_SOCKET.t_flags], TF_SENTFIN - jz TCP_send + jz .send mov ebx, [eax + TCP_SOCKET.SND_NXT] cmp ebx, [eax + TCP_SOCKET.SND_UNA] - je TCP_send + je .send ;-------------------- ; Enter persist state (191) @@ -298,13 +300,6 @@ TCP_output: ret - - - - - - - ;----------------------------------------------- ; ; Send a segment (222) @@ -314,8 +309,7 @@ TCP_output: ; dl = flags ; ;----------------------------------------------- -align 4 -TCP_send: + .send: DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl @@ -406,16 +400,48 @@ TCP_send: jbe .no_overflow mov esi, [eax + TCP_SOCKET.t_maxseg] - or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT + or [temp_bits], TCP_BIT_SENDALOT .no_overflow: +;---------------------------------------------------- +; Calculate the receive window. +; Dont shrink window, but avoid silly window syndrome + + mov ebx, SOCKET_MAXDATA + sub ebx, [eax + STREAM_SOCKET.rcv.size] + + cmp ebx, SOCKET_MAXDATA/4 + jae @f + cmp ebx, [eax + TCP_SOCKET.t_maxseg] + jae @f + xor ebx, ebx + @@: + + cmp ebx, TCP_max_win + jbe @f + mov ebx, TCP_max_win + @@: + + mov ecx, [eax + TCP_SOCKET.RCV_ADV] + sub ecx, [eax + TCP_SOCKET.RCV_NXT] + cmp ebx, ecx + ja @f + mov ebx, ecx + @@: + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: window = %u\n", ebx + + mov cl, [eax + TCP_SOCKET.RCV_SCALE] + shr ebx, cl + xchg bl, bh + ;----------------------------------------------------------------- ; Start by pushing all TCP header values in reverse order on stack ; (essentially, creating the tcp header on the stack!) pushw 0 ; .UrgentPointer dw ? pushw 0 ; .Checksum dw ? - pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370) + pushw bx ; .Window dw ? shl edi, 2 ; .DataOffset db ? only 4 left-most bits shl dx, 8 or dx, di ; .Flags db ? @@ -534,7 +560,13 @@ TCP_send: ;-------------------- ; Create the checksum + xor dx, dx + test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_OUT + jnz .checksum_ok + TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) + + .checksum_ok: mov [esi + TCP_header.Checksum], dx ;---------------- @@ -566,7 +598,7 @@ TCP_send: push [eax + TCP_SOCKET.RCV_NXT] pop [eax + TCP_SOCKET.last_ack_sent] -; and flags +; clear the ACK flags and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) ;-------------- @@ -582,7 +614,7 @@ TCP_send: ;----------------------------- ; Check if we need more output - test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT + test [temp_bits], TCP_BIT_SENDALOT jnz TCP_output.again DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: success!\n" @@ -622,7 +654,4 @@ TCP_send: ret - - - - +endp \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/network/tcp_subr.inc b/kernel/branches/Kolibri-acpi/network/tcp_subr.inc index 3846a96018..91514a8d29 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp_subr.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp_subr.inc @@ -152,7 +152,9 @@ TCP_drop: mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED + push eax call TCP_output + pop eax ;;; TODO: update stats @@ -169,8 +171,35 @@ TCP_drop: +;------------------------- +; +; TCP_disconnect +; +; IN: eax = socket ptr +; OUT: eax = socket ptr / 0 +; +;------------------------- +align 4 +TCP_disconnect: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_disconnect: %x\n", eax + cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED + jb TCP_close ; Connection not yet synchronised, just get rid of the socket + +; TODO: implement LINGER + + call SOCKET_is_disconnecting + call TCP_usrclosed + + test eax, eax + jz @f + push eax + call TCP_output + pop eax + @@: + + ret ;------------------------- @@ -178,7 +207,7 @@ TCP_drop: ; TCP_close ; ; IN: eax = socket ptr -; OUT: eax = socket ptr +; OUT: / ; ;------------------------- align 4 @@ -190,7 +219,9 @@ TCP_close: ;;; TODO: update slow start threshold call SOCKET_is_disconnected -;; call SOCKET_free + call SOCKET_free + + xor eax, eax ret diff --git a/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc b/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc index 9c3e8db0a4..65128da742 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc @@ -1,6 +1,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; -;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; Part of the TCP/IP network stack for KolibriOS ;; @@ -57,7 +57,6 @@ TCP_usrclosed: .wait1: mov [eax + TCP_SOCKET.t_state], TCPS_FIN_WAIT_1 - ; TODO: set timer? pop ebx ret @@ -68,7 +67,6 @@ TCP_usrclosed: .disc: call SOCKET_is_disconnected - ; TODO: set timer? .ret: pop ebx ret @@ -202,35 +200,4 @@ TCP_connect: stdcall cancel_timer_hs, [eax + TCP_SOCKET.timer_connect] xor eax, eax - ret - - - - -;------------------------- -; -; TCP_disconnect -; -; IN: eax = socket ptr -; OUT: eax = socket ptr -; -;------------------------- -align 4 -TCP_disconnect: - - DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_disconnect: %x\n", eax - - cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED - jb TCP_close - - -; TODO: implement LINGER ? - - call SOCKET_is_disconnecting - call TCP_usrclosed - - push eax - call TCP_output - pop eax - ret \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/video/cursors.inc b/kernel/branches/Kolibri-acpi/video/cursors.inc index d51e2f345c..8839830f09 100644 --- a/kernel/branches/Kolibri-acpi/video/cursors.inc +++ b/kernel/branches/Kolibri-acpi/video/cursors.inc @@ -295,6 +295,8 @@ proc set_cursor stdcall, hcursor:dword ; jne .fail mov ebx, [current_slot] xchg eax, [ebx+APPDATA.cursor] + mov [redrawmouse_unconditional], 1 + call __sys_draw_pointer ret ;-------------------------------------- align 4 diff --git a/kernel/branches/Kolibri-acpi/video/vesa20.inc b/kernel/branches/Kolibri-acpi/video/vesa20.inc index b3b778c109..1f84201ea8 100644 --- a/kernel/branches/Kolibri-acpi/video/vesa20.inc +++ b/kernel/branches/Kolibri-acpi/video/vesa20.inc @@ -1952,7 +1952,6 @@ align 4 mov esi, bgr_next_line mov edi, bgr_cur_line mov ecx, [_display.width] - inc ecx rep movsd jmp bgr_resmooth1 ;--------------------------------------