Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d0f4f2d3 | ||
|
|
0f5f56536b |
No files matched your search
+143
-13
@@ -1,6 +1,6 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2026. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License ;;
|
||||
;; ;;
|
||||
;; AMD PCnet driver for KolibriOS ;;
|
||||
@@ -31,6 +31,11 @@ entry START
|
||||
TX_RING_SIZE = 32 ; Number of packets in send ring buffer
|
||||
RX_RING_SIZE = 32 ; Number of packets in receive ring buffer
|
||||
|
||||
; pending transmit queue, used when frames arrive while hardware is busy
|
||||
PENDING_TX_MAX = 8 ; Maximum number of frames in the queue
|
||||
PENDING_TX_LOWAT = 0 ; Low-water mark for the number of frames in the queue, before resetting the NET_TX_BLOCKED flag
|
||||
|
||||
|
||||
; end configureable area
|
||||
|
||||
section '.flat' readable writable executable
|
||||
@@ -316,6 +321,11 @@ struct device ETH_DEVICE
|
||||
cur_tx dd ?
|
||||
last_tx dd ?
|
||||
|
||||
; pending transmit queue: linked list of frames that arrived while the hardware was busy
|
||||
pend_tx_head dd ? ; pointer to oldest queued NET_BUFF, 0 if empty
|
||||
pend_tx_tail dd ? ; pointer to newest queued NET_BUFF, 0 if empty
|
||||
pend_tx_count dd ? ; 0..PENDING_TX_MAX
|
||||
|
||||
options dd ?
|
||||
full_duplex db ?
|
||||
chip_version dw ?
|
||||
@@ -1000,6 +1010,21 @@ init_ring:
|
||||
mov [ebx + device.last_tx], 0
|
||||
mov [ebx + device.cur_rx], 0
|
||||
|
||||
mov esi, [ebx + device.pend_tx_head]
|
||||
.drain_pending:
|
||||
test esi, esi
|
||||
jz .pending_drained
|
||||
mov eax, [esi + NET_BUFF.NextPtr]
|
||||
push eax
|
||||
invoke NetFree, esi
|
||||
pop eax
|
||||
mov esi, eax
|
||||
jmp .drain_pending
|
||||
.pending_drained:
|
||||
mov dword [ebx + device.pend_tx_head], 0
|
||||
mov dword [ebx + device.pend_tx_tail], 0
|
||||
mov dword [ebx + device.pend_tx_count], 0
|
||||
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
@@ -1045,6 +1070,9 @@ proc transmit stdcall bufferptr
|
||||
|
||||
test [edi + descriptor.status], TXCTL_OWN
|
||||
jnz .overrun
|
||||
cmp [edi + descriptor.virtual], 0 ; Make sure int handler has performed cleanup already
|
||||
jne .desc_busy
|
||||
.desc_free:
|
||||
; descriptor is free, use it
|
||||
mov [edi + descriptor.virtual], esi
|
||||
mov eax, esi
|
||||
@@ -1058,6 +1086,12 @@ proc transmit stdcall bufferptr
|
||||
; put to transfer queue
|
||||
mov [edi + descriptor.status], TXCTL_OWN + TXCTL_STP + TXCTL_ENP
|
||||
|
||||
; Update stats
|
||||
inc [ebx + device.packets_tx]
|
||||
mov eax, [esi + NET_BUFF.length]
|
||||
add dword[ebx + device.bytes_tx], eax
|
||||
adc dword[ebx + device.bytes_tx + 4], 0
|
||||
|
||||
; trigger an immediate send
|
||||
mov edx, [ebx + device.io_addr]
|
||||
xor ecx, ecx ; CSR0
|
||||
@@ -1069,16 +1103,16 @@ proc transmit stdcall bufferptr
|
||||
inc [ebx + device.cur_tx]
|
||||
and [ebx + device.cur_tx], TX_RING_SIZE - 1
|
||||
|
||||
; Update stats
|
||||
inc [ebx + device.packets_tx]
|
||||
mov eax, [esi + NET_BUFF.length]
|
||||
add dword[ebx + device.bytes_tx], eax
|
||||
adc dword[ebx + device.bytes_tx + 4], 0
|
||||
|
||||
spin_unlock_irqrestore
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.desc_busy:
|
||||
pusha ; TODO CHECKME
|
||||
invoke NetFree, [edi + descriptor.virtual]
|
||||
popa
|
||||
jmp .desc_free
|
||||
|
||||
.error:
|
||||
DEBUGF 2, "TX packet error\n"
|
||||
inc [ebx + device.packets_tx_err]
|
||||
@@ -1089,12 +1123,40 @@ proc transmit stdcall bufferptr
|
||||
ret
|
||||
|
||||
.overrun:
|
||||
; Hardware ring is full.
|
||||
DEBUGF 1, "High water\n"
|
||||
or [ebx + device.state], NET_TX_BLOCKED
|
||||
|
||||
; Try the pending tx queue as a last resort
|
||||
cmp [ebx + device.pend_tx_count], PENDING_TX_MAX
|
||||
jae .queue_full
|
||||
|
||||
mov dword [esi + NET_BUFF.NextPtr], 0
|
||||
mov dword [esi + NET_BUFF.PrevPtr], 0
|
||||
mov eax, [ebx + device.pend_tx_tail]
|
||||
mov [ebx + device.pend_tx_tail], esi
|
||||
test eax, eax
|
||||
jz @f
|
||||
mov [eax + NET_BUFF.NextPtr], esi
|
||||
jmp .list_linked
|
||||
@@:
|
||||
mov [ebx + device.pend_tx_head], esi
|
||||
.list_linked:
|
||||
inc [ebx + device.pend_tx_count]
|
||||
DEBUGF 1, "Pending TX: %u\n", [ebx + device.pend_tx_count]
|
||||
|
||||
spin_unlock_irqrestore
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.queue_full:
|
||||
DEBUGF 2, "TX overrun\n"
|
||||
inc [ebx + device.packets_tx_ovr]
|
||||
invoke NetFree, [bufferptr]
|
||||
|
||||
spin_unlock_irqrestore
|
||||
or eax, -1
|
||||
dec eax
|
||||
ret
|
||||
|
||||
endp
|
||||
@@ -1225,20 +1287,20 @@ int_handler:
|
||||
jz .not_transmit
|
||||
|
||||
.tx_loop:
|
||||
lea edi, [ebx + device.tx_ring]
|
||||
mov eax, [ebx + device.last_tx]
|
||||
|
||||
lea edi, [ebx + device.tx_ring]
|
||||
shl eax, 4
|
||||
add edi, eax
|
||||
|
||||
test [edi + descriptor.status], TXCTL_OWN
|
||||
jnz .not_transmit
|
||||
jnz .tx_done ; still busy
|
||||
|
||||
mov eax, [edi + descriptor.virtual]
|
||||
test eax, eax
|
||||
jz .not_transmit
|
||||
jz .tx_done ; never filled, or already reclaimed
|
||||
|
||||
mov [edi + descriptor.virtual], 0
|
||||
|
||||
DEBUGF 1,"Removing packet %x from memory\n", eax
|
||||
|
||||
invoke NetFree, eax
|
||||
@@ -1247,6 +1309,10 @@ int_handler:
|
||||
and [ebx + device.last_tx], TX_RING_SIZE - 1
|
||||
jmp .tx_loop
|
||||
|
||||
.tx_done:
|
||||
cmp [ebx + device.pend_tx_count], 0
|
||||
jne .refill_loop
|
||||
|
||||
.not_transmit:
|
||||
pop edi esi ebx
|
||||
xor eax, eax
|
||||
@@ -1254,6 +1320,70 @@ int_handler:
|
||||
|
||||
ret
|
||||
|
||||
.refill_loop:
|
||||
mov ecx, [ebx + device.cur_tx]
|
||||
shl ecx, 4
|
||||
lea edi, [ebx + device.tx_ring]
|
||||
add edi, ecx
|
||||
|
||||
test [edi + descriptor.status], TXCTL_OWN
|
||||
jnz .refill_done ; not actually free after all - stop
|
||||
|
||||
cmp [edi + descriptor.virtual], 0
|
||||
je @f
|
||||
pusha
|
||||
invoke NetFree, [edi + descriptor.virtual]
|
||||
popa
|
||||
@@:
|
||||
|
||||
mov esi, [ebx + device.pend_tx_head]
|
||||
mov eax, [esi + NET_BUFF.NextPtr]
|
||||
mov [ebx + device.pend_tx_head], eax
|
||||
|
||||
mov [edi + descriptor.virtual], esi
|
||||
mov eax, esi
|
||||
add eax, [eax + NET_BUFF.offset]
|
||||
invoke GetPhysAddr
|
||||
mov [edi + descriptor.base], eax
|
||||
|
||||
mov eax, [esi + NET_BUFF.length]
|
||||
neg eax
|
||||
mov [edi + descriptor.length], ax
|
||||
|
||||
mov [edi + descriptor.status], TXCTL_OWN + TXCTL_STP + TXCTL_ENP
|
||||
|
||||
inc [ebx + device.packets_tx]
|
||||
mov eax, [esi + NET_BUFF.length]
|
||||
add dword[ebx + device.bytes_tx], eax
|
||||
adc dword[ebx + device.bytes_tx + 4], 0
|
||||
|
||||
inc [ebx + device.cur_tx]
|
||||
and [ebx + device.cur_tx], TX_RING_SIZE - 1
|
||||
|
||||
dec [ebx + device.pend_tx_count]
|
||||
jnz .refill_loop
|
||||
|
||||
mov dword [ebx + device.pend_tx_tail], 0
|
||||
|
||||
.refill_done:
|
||||
DEBUGF 1, "Pending TX: %u\n", [ebx + device.pend_tx_count]
|
||||
|
||||
mov edx, [ebx + device.io_addr]
|
||||
xor ecx, ecx ; CSR0
|
||||
call [ebx + device.read_csr]
|
||||
or eax, CSR_TX
|
||||
call [ebx + device.write_csr]
|
||||
|
||||
cmp [ebx + device.pend_tx_count], PENDING_TX_LOWAT
|
||||
ja @f
|
||||
and [ebx + device.state], not NET_TX_BLOCKED
|
||||
DEBUGF 1, "Low water\n"
|
||||
@@:
|
||||
pop edi esi ebx
|
||||
xor eax, eax
|
||||
inc eax
|
||||
|
||||
ret
|
||||
|
||||
|
||||
|
||||
@@ -1595,7 +1725,7 @@ proc check_media_mii stdcall dev:dword
|
||||
mov ecx, eax
|
||||
and eax, BMSR_LSTATUS
|
||||
shr eax, 2
|
||||
cmp eax, [ebx + device.state]
|
||||
cmp ax, word[ebx + device.state] ; lazy masking of NET_TX_BLOCKED
|
||||
jne .changed
|
||||
|
||||
spin_unlock_irqrestore
|
||||
@@ -1643,7 +1773,7 @@ proc check_media_mii stdcall dev:dword
|
||||
mov eax, ETH_LINK_UNKNOWN
|
||||
|
||||
.update:
|
||||
mov [ebx + device.state], eax
|
||||
mov word[ebx + device.state], ax ; lazy masking of NET_TX_BLOCKED
|
||||
invoke NetLinkChanged
|
||||
|
||||
|
||||
|
||||
+158
-154
@@ -1,154 +1,158 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License ;;
|
||||
;; ;;
|
||||
;; GNU GENERAL PUBLIC LICENSE ;;
|
||||
;; Version 2, June 1991 ;;
|
||||
;; ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; This macro will prepend driver name to all debug output through DEBUGF macro
|
||||
; The driver name is taken from my_service label
|
||||
|
||||
if defined my_service
|
||||
|
||||
macro DEBUGF _level,_format, [args] {
|
||||
common DEBUGF _level, "%s: " # _format, my_service, args
|
||||
}
|
||||
|
||||
end if
|
||||
|
||||
include 'pci.inc'
|
||||
include 'mii.inc'
|
||||
|
||||
; Kernel variables
|
||||
|
||||
PAGESIZE = 4096
|
||||
|
||||
; Network driver types
|
||||
|
||||
NET_TYPE_ETH = 1
|
||||
NET_TYPE_SLIP = 2
|
||||
|
||||
; Link state
|
||||
|
||||
ETH_LINK_DOWN = 0 ; Link is down
|
||||
ETH_LINK_UNKNOWN = 1b ; There could be an active link
|
||||
ETH_LINK_FULL_DUPLEX = 10b ; full duplex flag
|
||||
|
||||
ETH_LINK_SPEED_10M = 100b ; 10 Mbit
|
||||
ETH_LINK_SPEED_100M = 1000b ; 100 Mbit
|
||||
ETH_LINK_SPEED_1G = 1100b ; Gigabit
|
||||
|
||||
ETH_LINK_SPEED_MASK = 1100b
|
||||
|
||||
; Macro to easily set i/o addresses to access device.
|
||||
; In the beginning of a procedure (or ofter edx may have been destroyed),
|
||||
; always use set_io with offset 0 to reset the variables.
|
||||
|
||||
LAST_IO = 0
|
||||
|
||||
macro set_io baseaddr, offset {
|
||||
|
||||
if offset = 0
|
||||
mov edx, baseaddr
|
||||
else if offset = LAST_IO
|
||||
else
|
||||
add edx, offset - LAST_IO
|
||||
end if
|
||||
|
||||
LAST_IO = offset
|
||||
}
|
||||
|
||||
; Macro to allocate a contiguous buffer in memory
|
||||
; And initialise it to all zeros
|
||||
|
||||
; This macro will destroy eax, ecx and edi !
|
||||
|
||||
macro allocate_and_clear dest, size, err {
|
||||
|
||||
; We need to allocate at least 8 pages, if we want a contiguous area in ram
|
||||
push edx
|
||||
if (size < 8*4096) & (size > 4096)
|
||||
invoke KernelAlloc, 8*4096
|
||||
else
|
||||
invoke KernelAlloc, size
|
||||
end if
|
||||
pop edx
|
||||
|
||||
test eax, eax
|
||||
jz err
|
||||
mov dest, eax
|
||||
mov edi, eax ; look at last part of code!
|
||||
|
||||
; Release the unused pages (if any)
|
||||
if (size < 8*4096) & (size > 4096)
|
||||
add eax, (size/4096+1)*4096
|
||||
mov ecx, 8-(size/4096+1)
|
||||
push edx
|
||||
invoke ReleasePages
|
||||
pop edx
|
||||
end if
|
||||
|
||||
; Clear the allocated buffer
|
||||
mov ecx, size/4 ; divide by 4 because of DWORD
|
||||
xor eax, eax
|
||||
rep stosd
|
||||
|
||||
if (size - size/4*4)
|
||||
mov ecx, size - size/4*4
|
||||
rep stosb
|
||||
end if
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct NET_DEVICE
|
||||
|
||||
type dd ? ; Type field
|
||||
mtu dd ? ; Maximal Transmission Unit
|
||||
name dd ? ; Ptr to 0 terminated string
|
||||
|
||||
unload dd ? ; Ptrs to driver functions
|
||||
reset dd ? ;
|
||||
transmit dd ? ;
|
||||
|
||||
state dd ? ; link state (0 = no link)
|
||||
hwacc dd ? ; bitmask stating enabled HW accelerations (offload engines)
|
||||
|
||||
bytes_tx dq ? ; Statistics, updated by the driver
|
||||
bytes_rx dq ? ;
|
||||
|
||||
packets_tx dd ? ;
|
||||
packets_tx_err dd ? ; CRC errors, too long or too short frames
|
||||
packets_tx_drop dd ? ;
|
||||
packets_tx_ovr dd ? ; FIFO overrun
|
||||
|
||||
packets_rx dd ? ;
|
||||
packets_rx_err dd ? ; CRC errors, too long or too short frames
|
||||
packets_rx_drop dd ? ;
|
||||
packets_rx_ovr dd ? ; FIFO overrun
|
||||
|
||||
ends
|
||||
|
||||
|
||||
struct NET_BUFF
|
||||
|
||||
NextPtr dd ? ; pointer to next frame in list
|
||||
PrevPtr dd ? ; pointer to previous frame in list
|
||||
device dd ? ; ptr to NET_DEVICE structure
|
||||
type dd ? ; data type (e.g. Ethernet)
|
||||
length dd ? ; data length
|
||||
offset dd ? ; offset to actual data (24 bytes for default frame)
|
||||
data rb 0
|
||||
|
||||
ends
|
||||
|
||||
|
||||
struct ETH_DEVICE NET_DEVICE
|
||||
|
||||
mac dp ?
|
||||
dw ? ; qword alignment
|
||||
|
||||
ends
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License ;;
|
||||
;; ;;
|
||||
;; GNU GENERAL PUBLIC LICENSE ;;
|
||||
;; Version 2, June 1991 ;;
|
||||
;; ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; This macro will prepend driver name to all debug output through DEBUGF macro
|
||||
; The driver name is taken from my_service label
|
||||
|
||||
if defined my_service
|
||||
|
||||
macro DEBUGF _level,_format, [args] {
|
||||
common DEBUGF _level, "%s: " # _format, my_service, args
|
||||
}
|
||||
|
||||
end if
|
||||
|
||||
include 'pci.inc'
|
||||
include 'mii.inc'
|
||||
|
||||
; Kernel variables
|
||||
|
||||
PAGESIZE = 4096
|
||||
|
||||
; Network driver types
|
||||
|
||||
NET_TYPE_ETH = 1
|
||||
NET_TYPE_SLIP = 2
|
||||
|
||||
; Link state
|
||||
|
||||
ETH_LINK_DOWN = 0 ; Link is down
|
||||
ETH_LINK_UNKNOWN = 1b ; Link is up, but speed might be unknown
|
||||
ETH_LINK_FULL_DUPLEX = 10b ; full duplex flag
|
||||
|
||||
ETH_LINK_SPEED_10M = 100b ; 10 Mbit
|
||||
ETH_LINK_SPEED_100M = 1000b ; 100 Mbit
|
||||
ETH_LINK_SPEED_1G = 1100b ; Gigabit
|
||||
|
||||
ETH_LINK_SPEED_MASK = 1100b
|
||||
|
||||
; Hardware state
|
||||
|
||||
NET_TX_BLOCKED = 1 shl 31 ; Transmit queue is full, if you call transmit again, driver might drop your frame
|
||||
|
||||
; Macro to easily set i/o addresses to access device.
|
||||
; In the beginning of a procedure (or ofter edx may have been destroyed),
|
||||
; always use set_io with offset 0 to reset the variables.
|
||||
|
||||
LAST_IO = 0
|
||||
|
||||
macro set_io baseaddr, offset {
|
||||
|
||||
if offset = 0
|
||||
mov edx, baseaddr
|
||||
else if offset = LAST_IO
|
||||
else
|
||||
add edx, offset - LAST_IO
|
||||
end if
|
||||
|
||||
LAST_IO = offset
|
||||
}
|
||||
|
||||
; Macro to allocate a contiguous buffer in memory
|
||||
; And initialise it to all zeros
|
||||
|
||||
; This macro will destroy eax, ecx and edi !
|
||||
|
||||
macro allocate_and_clear dest, size, err {
|
||||
|
||||
; We need to allocate at least 8 pages, if we want a contiguous area in ram
|
||||
push edx
|
||||
if (size < 8*4096) & (size > 4096)
|
||||
invoke KernelAlloc, 8*4096
|
||||
else
|
||||
invoke KernelAlloc, size
|
||||
end if
|
||||
pop edx
|
||||
|
||||
test eax, eax
|
||||
jz err
|
||||
mov dest, eax
|
||||
mov edi, eax ; look at last part of code!
|
||||
|
||||
; Release the unused pages (if any)
|
||||
if (size < 8*4096) & (size > 4096)
|
||||
add eax, (size/4096+1)*4096
|
||||
mov ecx, 8-(size/4096+1)
|
||||
push edx
|
||||
invoke ReleasePages
|
||||
pop edx
|
||||
end if
|
||||
|
||||
; Clear the allocated buffer
|
||||
mov ecx, size/4 ; divide by 4 because of DWORD
|
||||
xor eax, eax
|
||||
rep stosd
|
||||
|
||||
if (size - size/4*4)
|
||||
mov ecx, size - size/4*4
|
||||
rep stosb
|
||||
end if
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct NET_DEVICE
|
||||
|
||||
type dd ? ; Type field
|
||||
mtu dd ? ; Maximal Transmission Unit
|
||||
name dd ? ; Ptr to 0 terminated string
|
||||
|
||||
unload dd ? ; Ptrs to driver functions
|
||||
reset dd ? ;
|
||||
transmit dd ? ;
|
||||
|
||||
state dd ? ; link state (0 = no link)
|
||||
hwacc dd ? ; bitmask stating enabled HW accelerations (offload engines)
|
||||
|
||||
bytes_tx dq ? ; Statistics, updated by the driver
|
||||
bytes_rx dq ? ;
|
||||
|
||||
packets_tx dd ? ;
|
||||
packets_tx_err dd ? ; CRC errors, too long or too short frames
|
||||
packets_tx_drop dd ? ;
|
||||
packets_tx_ovr dd ? ; FIFO overrun
|
||||
|
||||
packets_rx dd ? ;
|
||||
packets_rx_err dd ? ; CRC errors, too long or too short frames
|
||||
packets_rx_drop dd ? ;
|
||||
packets_rx_ovr dd ? ; FIFO overrun
|
||||
|
||||
ends
|
||||
|
||||
|
||||
struct NET_BUFF
|
||||
|
||||
NextPtr dd ? ; pointer to next frame in list
|
||||
PrevPtr dd ? ; pointer to previous frame in list
|
||||
device dd ? ; ptr to NET_DEVICE structure
|
||||
type dd ? ; data type (e.g. Ethernet)
|
||||
length dd ? ; data length
|
||||
offset dd ? ; offset to actual data (24 bytes for default frame)
|
||||
data rb 0
|
||||
|
||||
ends
|
||||
|
||||
|
||||
struct ETH_DEVICE NET_DEVICE
|
||||
|
||||
mac dp ?
|
||||
dw ? ; qword alignment
|
||||
|
||||
ends
|
||||
+30
-138
@@ -1,6 +1,6 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2026. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License ;;
|
||||
;; ;;
|
||||
;; Part of the TCP/IP network stack for KolibriOS ;;
|
||||
@@ -279,10 +279,8 @@ sys_socket:
|
||||
dd socket_get_opt ; 9
|
||||
dd socket_pair ; 10
|
||||
|
||||
dd socket_getpeername ; 11
|
||||
dd socket_getsockname ; 12
|
||||
;dd socket_sendto ; 13
|
||||
;dd socket_recvfrom ; 14
|
||||
;dd socket_sendto ; 11
|
||||
;dd socket_recvfrom ; 12
|
||||
.number = ($ - .table) / 4 - 1
|
||||
|
||||
.error:
|
||||
@@ -1036,6 +1034,9 @@ socket_send:
|
||||
test eax, eax
|
||||
jz .invalid
|
||||
|
||||
test esi, esi
|
||||
jz .zero_length
|
||||
|
||||
stdcall is_region_userspace, edx, esi
|
||||
jnz .invalid
|
||||
|
||||
@@ -1049,6 +1050,10 @@ socket_send:
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
ret
|
||||
|
||||
.zero_length:
|
||||
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_send: data length = 0\n"
|
||||
mov dword[esp + SYSCALL_STACK.eax], 0
|
||||
ret
|
||||
|
||||
align 4
|
||||
socket_send_udp:
|
||||
@@ -1072,11 +1077,15 @@ socket_send_tcp:
|
||||
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: TCP\n"
|
||||
|
||||
.loop:
|
||||
push eax
|
||||
add eax, STREAM_SOCKET.snd
|
||||
call socket_ring_write
|
||||
pop eax
|
||||
|
||||
test ecx, ecx
|
||||
jz .block
|
||||
|
||||
mov [esp + SYSCALL_STACK.eax], ecx
|
||||
mov [eax + SOCKET.errorcode], 0
|
||||
push eax
|
||||
@@ -1086,6 +1095,22 @@ socket_send_tcp:
|
||||
mov [esp + SYSCALL_STACK.ebx], eax
|
||||
ret
|
||||
|
||||
.block:
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: TCP is blocked\n"
|
||||
test [eax + SOCKET.options], SO_NONBLOCK
|
||||
jnz .wouldblock
|
||||
|
||||
call socket_block
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: TCP is unblocked\n"
|
||||
mov ecx, dword[esp + SYSCALL_STACK.esi]
|
||||
jmp .loop
|
||||
|
||||
.wouldblock:
|
||||
mov dword[esp + SYSCALL_STACK.ebx], EWOULDBLOCK
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
pop esi edx
|
||||
ret
|
||||
|
||||
|
||||
align 4
|
||||
socket_send_ip:
|
||||
@@ -1398,139 +1423,6 @@ socket_pair:
|
||||
|
||||
|
||||
|
||||
;-----------------------------------------------------------------;
|
||||
; ;
|
||||
; socket_getpeername: Get name of connected peer socket. ;
|
||||
; ;
|
||||
; IN: ecx = socket number ;
|
||||
; edx = pointer to sockaddr struct ;
|
||||
; esi = length of sockaddr struct ;
|
||||
; ;
|
||||
; OUT: eax = length of sockaddr struct ;
|
||||
; eax = -1 on error ;
|
||||
; ebx = errorcode on error ;
|
||||
; ;
|
||||
;-----------------------------------------------------------------;
|
||||
align 4
|
||||
socket_getpeername:
|
||||
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_getpeername: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
|
||||
|
||||
call socket_num_to_ptr
|
||||
test eax, eax
|
||||
jz .notsock
|
||||
|
||||
stdcall is_region_userspace, edx, esi
|
||||
jnz .efault
|
||||
|
||||
cmp [eax + SOCKET.Domain], AF_INET4
|
||||
jne .invalid
|
||||
|
||||
cmp esi, 8 ; domain + port + ipv4_addr
|
||||
jb .errlen
|
||||
|
||||
mov word[edx], AF_INET4
|
||||
mov esi, edx
|
||||
|
||||
xor edx, edx
|
||||
cmp [eax + SOCKET.Type], SOCK_RAW
|
||||
je .raw_protocol
|
||||
|
||||
mov dx, [eax + TCP_SOCKET.RemotePort]
|
||||
.raw_protocol:
|
||||
cmp [eax + SOCKET.state], SS_ISCONNECTED
|
||||
jne .notconn
|
||||
|
||||
mov [esi + sockaddr.port], dx
|
||||
|
||||
mov edx, [eax + IP_SOCKET.RemoteIP]
|
||||
mov [esi + sockaddr.ip], edx
|
||||
|
||||
mov dword[esp + SYSCALL_STACK.eax], 8
|
||||
ret
|
||||
|
||||
.efault:
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
mov dword[esp + SYSCALL_STACK.ebx], EFAULT
|
||||
ret
|
||||
|
||||
.notconn:
|
||||
mov dword[esp + SYSCALL_STACK.ebx], ENOTCONN
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
ret
|
||||
.no_inet4:
|
||||
.errlen:
|
||||
.invalid:
|
||||
mov dword[esp + SYSCALL_STACK.ebx], EINVAL
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
ret
|
||||
.notsock:
|
||||
mov dword[esp + SYSCALL_STACK.ebx], ENOTSOCK
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
ret
|
||||
;-----------------------------------------------------------------;
|
||||
; ;
|
||||
; socket_getsockname: Get socket name. ;
|
||||
; ;
|
||||
; IN: ecx = socket number ;
|
||||
; edx = pointer to sockaddr struct ;
|
||||
; esi = length of sockaddr struct ;
|
||||
; ;
|
||||
; OUT: eax = length of sockaddr struct ;
|
||||
; eax = -1 on error ;
|
||||
; ebx = errorcode on error ;
|
||||
; ;
|
||||
;-----------------------------------------------------------------;
|
||||
align 4
|
||||
socket_getsockname:
|
||||
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_getsockname: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
|
||||
|
||||
call socket_num_to_ptr
|
||||
test eax, eax
|
||||
jz .notsock
|
||||
|
||||
stdcall is_region_userspace, edx, esi
|
||||
jnz .efault
|
||||
|
||||
cmp [eax + SOCKET.Domain], AF_INET4
|
||||
jne .invalid
|
||||
|
||||
cmp esi, 8 ; domain + port + ipv4_addr
|
||||
jb .errlen
|
||||
|
||||
mov word[edx], AF_INET4
|
||||
mov esi, edx
|
||||
|
||||
xor edx, edx
|
||||
cmp [eax + SOCKET.Type], SOCK_RAW
|
||||
je .raw_protocol
|
||||
|
||||
mov dx, [eax + TCP_SOCKET.RemotePort]
|
||||
.raw_protocol:
|
||||
mov [esi + sockaddr.port], dx
|
||||
|
||||
mov edx, [eax + IP_SOCKET.RemoteIP]
|
||||
mov [esi + sockaddr.ip], edx
|
||||
|
||||
mov dword[esp + SYSCALL_STACK.eax], 8
|
||||
ret
|
||||
|
||||
.efault:
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
mov dword[esp + SYSCALL_STACK.ebx], EFAULT
|
||||
ret
|
||||
|
||||
.errlen:
|
||||
.invalid:
|
||||
mov dword[esp + SYSCALL_STACK.ebx], EINVAL
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
ret
|
||||
.notsock:
|
||||
mov dword[esp + SYSCALL_STACK.ebx], ENOTSOCK
|
||||
mov dword[esp + SYSCALL_STACK.eax], -1
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------;
|
||||
; ;
|
||||
; socket_debug: Copy socket variables to application buffer. ;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2026. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License ;;
|
||||
;; ;;
|
||||
;; STACK.INC ;;
|
||||
@@ -139,9 +139,6 @@ EISCONN = 56
|
||||
ETIMEDOUT = 60
|
||||
ECONNREFUSED = 61
|
||||
|
||||
EFAULT = 14 ; copy, defined in posix/posix.inc ; Bad address
|
||||
ENOTSOCK = 38 ; FreeBSD error code, Socket operation on non-socket
|
||||
|
||||
; Api protocol numbers
|
||||
API_ETH = 0
|
||||
API_IPv4 = 1
|
||||
@@ -171,6 +168,9 @@ NET_HWACC_TCP_IPv4_OUT = 1 shl 1
|
||||
NET_BUFF_LOOPBACK = 0
|
||||
NET_BUFF_ETH = 1
|
||||
|
||||
; Hardware state (inside link_state)
|
||||
NET_TX_BLOCKED = 1 shl 31 ; Transmit queue is full, if you call transmit again, driver might drop your frame
|
||||
|
||||
struct NET_DEVICE
|
||||
|
||||
device_type dd ? ; Type field
|
||||
|
||||
@@ -355,14 +355,9 @@ endl
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Got window scale option\n"
|
||||
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
|
||||
|
||||
; remember the peer's scale factor; it is applied to SND_SCALE when the
|
||||
; connection is established (together with our RCV_SCALE)
|
||||
lodsb
|
||||
cmp al, TCP_max_winshift
|
||||
jbe .wscale_capped
|
||||
mov al, TCP_max_winshift
|
||||
.wscale_capped:
|
||||
mov [ebx + TCP_SOCKET.requested_s_scale], al
|
||||
mov [ebx + TCP_SOCKET.SND_SCALE], al
|
||||
;;;;; TODO
|
||||
|
||||
@@:
|
||||
jmp .opt_loop
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2026. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License ;;
|
||||
;; ;;
|
||||
;; Part of the TCP/IP network stack for KolibriOS ;;
|
||||
@@ -554,6 +554,14 @@ endl
|
||||
|
||||
mov ecx, esi
|
||||
mov ebx, [eax + IP_SOCKET.device]
|
||||
test ebx, ebx
|
||||
jz @f
|
||||
|
||||
; Check if the output path is blocked, dont generate output frames if so.
|
||||
test [ebx + NET_DEVICE.link_state], NET_TX_BLOCKED
|
||||
jnz .output_blocked
|
||||
@@:
|
||||
|
||||
mov edx, [eax + IP_SOCKET.LocalIP] ; source ip
|
||||
mov edi, [eax + IP_SOCKET.RemoteIP] ; dest ip
|
||||
mov al, [eax + IP_SOCKET.ttl]
|
||||
@@ -736,6 +744,23 @@ endl
|
||||
or eax, -1
|
||||
ret
|
||||
|
||||
.output_blocked:
|
||||
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: output path blocked, aborting\n"
|
||||
|
||||
pop ecx
|
||||
add esp, ecx
|
||||
add esp, 4
|
||||
pop eax
|
||||
|
||||
mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min
|
||||
or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
|
||||
|
||||
lea ecx, [eax + SOCKET.mutex]
|
||||
call mutex_unlock
|
||||
|
||||
or eax, -1
|
||||
ret
|
||||
|
||||
|
||||
.send_error:
|
||||
add esp, 4
|
||||
@@ -746,8 +771,8 @@ endl
|
||||
|
||||
DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: sending failed\n"
|
||||
|
||||
or eax, -2
|
||||
or eax, -1
|
||||
dec eax
|
||||
ret
|
||||
|
||||
|
||||
endp
|
||||
@@ -320,12 +320,12 @@ tcp_respond:
|
||||
stosb
|
||||
mov eax, SOCKET_BUFFER_SIZE
|
||||
sub eax, [esi + STREAM_SOCKET.rcv.size]
|
||||
cmp eax, TCP_max_win
|
||||
mov cl, [esi + TCP_SOCKET.RCV_SCALE]
|
||||
shr eax, cl ; scale first, then clamp to the
|
||||
cmp eax, TCP_max_win ; 16-bit field maximum
|
||||
jbe .lessthanmax
|
||||
mov eax, TCP_max_win
|
||||
.lessthanmax:
|
||||
mov cl, [esi + TCP_SOCKET.RCV_SCALE]
|
||||
shr eax, cl
|
||||
|
||||
xchg al, ah
|
||||
stosw ; window
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; kperf.asm - simple iperf-style TCP throughput tester ;;
|
||||
;; for KolibriOS ;;
|
||||
;; ;;
|
||||
;; Usage: ;;
|
||||
;; kperf -c <ip> connect to a server and blast data ;;
|
||||
;; kperf -s listen, accept, and receive data ;;
|
||||
;; ;;
|
||||
;; Compatible with real iperf2 in "-C" (compatibility) mode: ;;
|
||||
;; Linux side, receive test: iperf -s -p 5001 -i 1 ;;
|
||||
;; Linux side, send test: iperf -c <kolibri-ip> -p 5001 \ ;;
|
||||
;; -C -i 1 ;;
|
||||
;; No iperf header is sent or expected in either direction - it's ;;
|
||||
;; a raw byte-count throughput test, matching -C behaviour. ;;
|
||||
;; ;;
|
||||
;; Socket API and command-line parsing patterns verified against ;;
|
||||
;; tcpserv.asm and telnet.asm (KolibriOS team, hidnplayr). ;;
|
||||
;; The interval/summary reporting math below is new code and has ;;
|
||||
;; not been run - sanity check the numbers on first use. ;;
|
||||
;; ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
format binary as ""
|
||||
|
||||
PORT = 5001 ; classic iperf2 default port
|
||||
SENDBUFSIZE = 32768
|
||||
RECVBUFSIZE = 32768
|
||||
DURATION_CS = 1000 ; client test duration: 10.00 sec (centiseconds)
|
||||
REPORT_CS = 100 ; report interval: 1.00 sec (centiseconds)
|
||||
|
||||
use32
|
||||
; standard header
|
||||
db 'MENUET01' ; signature
|
||||
dd 1 ; header version
|
||||
dd start ; entry point
|
||||
dd i_end ; initialized size
|
||||
dd mem+4096 ; required memory
|
||||
dd mem+4096 ; stack pointer
|
||||
dd cmdline ; parameters - kernel writes ASCIIZ args here
|
||||
dd 0 ; path
|
||||
|
||||
include '../../macros.inc'
|
||||
purge mov,add,sub
|
||||
include '../../proc32.inc'
|
||||
include '../../dll.inc'
|
||||
include '../../network.inc'
|
||||
|
||||
; ======================================================================
|
||||
; entry point
|
||||
; ======================================================================
|
||||
start:
|
||||
stdcall dll.Load, @IMPORT
|
||||
test eax, eax
|
||||
jnz exit
|
||||
|
||||
invoke con_start, 1
|
||||
invoke con_init, 80, 25, 80, 25, title
|
||||
|
||||
mcall 40, EVM_STACK
|
||||
|
||||
cmp byte[cmdline], 0
|
||||
jne .have_args
|
||||
invoke con_write_asciiz, str_usage
|
||||
jmp done
|
||||
.have_args:
|
||||
|
||||
mov esi, cmdline
|
||||
cmp byte[esi], '-'
|
||||
jne .modechar
|
||||
inc esi
|
||||
.modechar:
|
||||
lodsb
|
||||
cmp al, 's'
|
||||
je server_mode
|
||||
cmp al, 'c'
|
||||
je client_prep
|
||||
invoke con_write_asciiz, str_usage
|
||||
jmp done
|
||||
|
||||
; ======================================================================
|
||||
; client mode - parse IP, connect, blast data
|
||||
; ======================================================================
|
||||
client_prep:
|
||||
.skipsp:
|
||||
cmp byte[esi], ' '
|
||||
jne .gotip
|
||||
inc esi
|
||||
jmp .skipsp
|
||||
.gotip:
|
||||
push esp
|
||||
invoke getaddrinfo, esi, 0, 0, esp
|
||||
pop edi
|
||||
test eax, eax
|
||||
jnz dns_err
|
||||
|
||||
mov eax, [edi+addrinfo.ai_addr]
|
||||
mov eax, [eax+sockaddr_in.sin_addr]
|
||||
mov [sockaddr1.ip], eax
|
||||
invoke freeaddrinfo, edi
|
||||
|
||||
invoke con_write_asciiz, str_connecting
|
||||
invoke inet_ntoa, [sockaddr1.ip]
|
||||
invoke con_write_asciiz, eax
|
||||
invoke con_write_asciiz, str_nl
|
||||
|
||||
mov ax, PORT
|
||||
xchg al, ah ; host->network byte order
|
||||
mov [sockaddr1.port], ax
|
||||
|
||||
mcall socket, AF_INET4, SOCK_STREAM, 0
|
||||
cmp eax, -1
|
||||
je socket_err
|
||||
mov [socketnum], eax
|
||||
|
||||
mcall connect, [socketnum], sockaddr1, 18
|
||||
test eax, eax
|
||||
jnz socket_err
|
||||
|
||||
invoke con_write_asciiz, str_connected
|
||||
|
||||
; --- timing setup ---
|
||||
mcall 26, 9
|
||||
mov [start_time], eax
|
||||
mov [last_report], eax
|
||||
mov dword[interval_bytes], 0
|
||||
mov dword[total_bytes], 0
|
||||
|
||||
.sendloop:
|
||||
invoke con_get_flags
|
||||
test eax, 0x200
|
||||
jnz .client_closed
|
||||
|
||||
mcall send, [socketnum], sendbuf, SENDBUFSIZE, 0
|
||||
cmp eax, -1
|
||||
je .client_closed
|
||||
;cmp eax, 0 ; 0 means buffer is full
|
||||
;je .client_closed
|
||||
|
||||
add [interval_bytes], eax
|
||||
add [total_bytes], eax
|
||||
|
||||
mcall 26, 9
|
||||
mov ecx, eax ; ecx = now
|
||||
mov ebx, ecx
|
||||
sub ebx, [last_report] ; ebx = elapsed since last report
|
||||
cmp ebx, REPORT_CS
|
||||
jl .check_dur
|
||||
|
||||
mov edx, [last_report]
|
||||
sub edx, [start_time] ; edx = interval start (rel. to test start)
|
||||
stdcall print_interval, [interval_bytes], ebx, edx, ecx
|
||||
mov [last_report], ecx
|
||||
mov dword[interval_bytes], 0
|
||||
|
||||
.check_dur:
|
||||
mcall 26, 9
|
||||
mov ebx, eax
|
||||
sub ebx, [start_time]
|
||||
cmp ebx, DURATION_CS
|
||||
jl .sendloop
|
||||
|
||||
mcall 26, 9
|
||||
mov ebx, eax
|
||||
sub ebx, [start_time]
|
||||
stdcall print_summary, [total_bytes], ebx
|
||||
jmp .client_done
|
||||
|
||||
.client_closed:
|
||||
invoke con_write_asciiz, str_conn_closed
|
||||
mov ebx, [last_report]
|
||||
sub ebx, [start_time]
|
||||
stdcall print_summary, [total_bytes], ebx
|
||||
|
||||
.client_done:
|
||||
mcall close, [socketnum]
|
||||
jmp done
|
||||
|
||||
; ======================================================================
|
||||
; server mode - listen, accept, receive data
|
||||
; ======================================================================
|
||||
server_mode:
|
||||
mcall socket, AF_INET4, SOCK_STREAM, 0
|
||||
cmp eax, -1
|
||||
je socket_err
|
||||
mov [socketnum], eax
|
||||
|
||||
mov ax, PORT
|
||||
xchg al, ah
|
||||
mov [sockaddr1.port], ax
|
||||
mov dword[sockaddr1.ip], 0
|
||||
|
||||
mcall bind, [socketnum], sockaddr1, 18
|
||||
cmp eax, -1
|
||||
je socket_err
|
||||
|
||||
mcall listen, [socketnum], 1
|
||||
cmp eax, -1
|
||||
je socket_err
|
||||
|
||||
invoke con_write_asciiz, str_listening
|
||||
|
||||
mcall accept, [socketnum], sockaddr1, 18
|
||||
cmp eax, -1
|
||||
je socket_err
|
||||
mov [socketnum2], eax
|
||||
|
||||
invoke con_write_asciiz, str_accepted
|
||||
invoke inet_ntoa, [sockaddr1.ip]
|
||||
invoke con_write_asciiz, eax
|
||||
invoke con_write_asciiz, str_nl
|
||||
|
||||
mcall 26, 9
|
||||
mov [start_time], eax
|
||||
mov [last_report], eax
|
||||
mov dword[interval_bytes], 0
|
||||
mov dword[total_bytes], 0
|
||||
|
||||
.recvloop:
|
||||
invoke con_get_flags
|
||||
test eax, 0x200
|
||||
jnz .server_closed
|
||||
|
||||
mcall recv, [socketnum2], recvbuf, RECVBUFSIZE, 0
|
||||
cmp eax, -1
|
||||
je .server_closed
|
||||
cmp eax, 0
|
||||
je .server_closed
|
||||
|
||||
add [interval_bytes], eax
|
||||
add [total_bytes], eax
|
||||
|
||||
mcall 26, 9
|
||||
mov ecx, eax
|
||||
mov ebx, ecx
|
||||
sub ebx, [last_report]
|
||||
cmp ebx, REPORT_CS
|
||||
jl .recvloop
|
||||
|
||||
mov edx, [last_report]
|
||||
sub edx, [start_time]
|
||||
stdcall print_interval, [interval_bytes], ebx, edx, ecx
|
||||
mov [last_report], ecx
|
||||
mov dword[interval_bytes], 0
|
||||
jmp .recvloop
|
||||
|
||||
.server_closed:
|
||||
invoke con_write_asciiz, str_conn_closed
|
||||
mcall 26, 9
|
||||
mov ebx, eax
|
||||
sub ebx, [start_time]
|
||||
stdcall print_summary, [total_bytes], ebx
|
||||
|
||||
mcall close, [socketnum2]
|
||||
jmp done
|
||||
|
||||
; ======================================================================
|
||||
; error handlers
|
||||
; ======================================================================
|
||||
socket_err:
|
||||
invoke con_write_asciiz, str_sockerr
|
||||
jmp done
|
||||
|
||||
dns_err:
|
||||
invoke con_write_asciiz, str_dnserr
|
||||
jmp done
|
||||
|
||||
done:
|
||||
invoke con_getch2
|
||||
invoke con_exit, 1
|
||||
exit:
|
||||
mcall -1
|
||||
|
||||
|
||||
; ======================================================================
|
||||
; print_interval: bytes, interval_cs, range_start_cs, range_end_cs
|
||||
; Prints "S.CC-S.CC sec X.XX Mbits/sec"
|
||||
; ======================================================================
|
||||
proc print_interval uses eax ebx ecx edx, pbytes, pinterval, prange_start, prange_end
|
||||
stdcall fmt_cs, [prange_start]
|
||||
invoke con_write_asciiz, linebuf
|
||||
invoke con_write_asciiz, str_dash
|
||||
stdcall fmt_cs, [prange_end]
|
||||
invoke con_write_asciiz, linebuf
|
||||
invoke con_write_asciiz, str_sec_tab
|
||||
|
||||
stdcall fmt_mbps, [pbytes], [pinterval]
|
||||
invoke con_write_asciiz, linebuf
|
||||
invoke con_write_asciiz, str_mbits_nl
|
||||
ret
|
||||
endp
|
||||
|
||||
; ======================================================================
|
||||
; print_summary: bytes, total_cs
|
||||
; Prints "0.00-S.CC sec (total) X.XX Mbits/sec"
|
||||
; ======================================================================
|
||||
proc print_summary uses eax ebx ecx edx, pbytes, pinterval
|
||||
invoke con_write_asciiz, str_zero
|
||||
invoke con_write_asciiz, str_dash
|
||||
stdcall fmt_cs, [pinterval]
|
||||
invoke con_write_asciiz, linebuf
|
||||
invoke con_write_asciiz, str_sec_total
|
||||
|
||||
stdcall fmt_mbps, [pbytes], [pinterval]
|
||||
invoke con_write_asciiz, linebuf
|
||||
invoke con_write_asciiz, str_mbits_nl
|
||||
ret
|
||||
endp
|
||||
|
||||
; ======================================================================
|
||||
; fmt_cs: format a centisecond dword value as "S.CC" into linebuf
|
||||
; ======================================================================
|
||||
proc fmt_cs uses eax ebx edx edi, pcs
|
||||
mov eax, [pcs]
|
||||
mov ebx, 100
|
||||
xor edx, edx
|
||||
div ebx ; eax = whole seconds, edx = centiseconds
|
||||
push edx ; save centiseconds
|
||||
mov edi, linebuf
|
||||
stdcall itoa, eax, edi
|
||||
mov edi, eax ; itoa returns pointer to end of written digits
|
||||
mov byte[edi], '.'
|
||||
inc edi
|
||||
pop eax ; eax = centiseconds (0-99)
|
||||
mov ebx, 10
|
||||
xor edx, edx
|
||||
div ebx ; eax = tens digit, edx = ones digit
|
||||
add al, '0'
|
||||
mov [edi], al
|
||||
inc edi
|
||||
add dl, '0'
|
||||
mov [edi], dl
|
||||
inc edi
|
||||
mov byte[edi], 0
|
||||
ret
|
||||
endp
|
||||
|
||||
; ======================================================================
|
||||
; fmt_mbps: format (bytes, interval_cs) as "X.XX" Mbits/sec into linebuf
|
||||
; Mbps*100 = (bytes * 8) / (interval_cs * 100)
|
||||
; ======================================================================
|
||||
proc fmt_mbps uses ebx ecx edx edi, pbytes, pinterval
|
||||
mov eax, [pbytes]
|
||||
cmp eax, 0x1FFFFFFF ; guard against *8 overflow at extreme rates
|
||||
jb .noshift
|
||||
shr eax, 4 ; scale down bytes and interval together
|
||||
mov ecx, [pinterval]
|
||||
shr ecx, 0 ; (kept for clarity; no shift on interval)
|
||||
jmp .have_scaled
|
||||
.noshift:
|
||||
mov ecx, [pinterval]
|
||||
.have_scaled:
|
||||
mov ebx, 8
|
||||
mul ebx ; edx:eax = bytes*8
|
||||
mov ebx, ecx
|
||||
imul ebx, 100 ; ebx = interval_cs * 100
|
||||
cmp ebx, 0
|
||||
jnz .divok
|
||||
mov ebx, 1 ; avoid div-by-zero on a zero-length interval
|
||||
.divok:
|
||||
div ebx ; eax = Mbps*100
|
||||
|
||||
mov edi, linebuf
|
||||
mov ebx, 100
|
||||
xor edx, edx
|
||||
div ebx ; eax = whole Mbps, edx = fractional (0-99)
|
||||
push edx
|
||||
stdcall itoa, eax, edi
|
||||
mov edi, eax
|
||||
mov byte[edi], '.'
|
||||
inc edi
|
||||
pop eax
|
||||
mov ebx, 10
|
||||
xor edx, edx
|
||||
div ebx
|
||||
add al, '0'
|
||||
mov [edi], al
|
||||
inc edi
|
||||
add dl, '0'
|
||||
mov [edi], dl
|
||||
inc edi
|
||||
mov byte[edi], 0
|
||||
ret
|
||||
endp
|
||||
|
||||
; ======================================================================
|
||||
; itoa: unsigned dword -> decimal ASCII, no leading zeros ("0" for zero)
|
||||
; in: value, dest buffer pointer
|
||||
; out: eax = pointer to the byte AFTER the last digit written
|
||||
; (buffer is NOT null-terminated by this routine)
|
||||
; ======================================================================
|
||||
proc itoa uses ebx ecx edx esi, value, dest
|
||||
mov eax, [value]
|
||||
mov edi, [dest]
|
||||
mov ecx, 0 ; digit count
|
||||
mov ebx, 10
|
||||
test eax, eax
|
||||
jnz .divloop
|
||||
mov byte[edi], '0'
|
||||
inc edi
|
||||
mov eax, edi
|
||||
ret
|
||||
.divloop:
|
||||
xor edx, edx
|
||||
div ebx
|
||||
push edx
|
||||
inc ecx
|
||||
test eax, eax
|
||||
jnz .divloop
|
||||
.popl:
|
||||
pop edx
|
||||
add dl, '0'
|
||||
mov [edi], dl
|
||||
inc edi
|
||||
loop .popl
|
||||
mov eax, edi
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
; data
|
||||
title db 'kperf',0
|
||||
|
||||
str_usage db 'Usage: kperf -c <ip> (client)',10,\
|
||||
' kperf -s (server)',10,10,0
|
||||
|
||||
str_connecting db 'Connecting to ',0
|
||||
str_connected db 'Connected. Starting test...',10,10,0
|
||||
str_listening db 'Listening on port 5001...',10,0
|
||||
str_accepted db 'Accepted connection from ',0
|
||||
str_conn_closed db 10,'Connection closed.',10,0
|
||||
str_sockerr db 'Socket error.',10,10,0
|
||||
str_dnserr db 'Could not parse address.',10,10,0
|
||||
str_nl db 10,0
|
||||
str_dash db '-',0
|
||||
str_zero db '0.00',0
|
||||
str_sec_tab db ' sec ',0
|
||||
str_sec_total db ' sec (total) ',0
|
||||
str_mbits_nl db ' Mbits/sec',10,0
|
||||
|
||||
sockaddr1:
|
||||
dw AF_INET4
|
||||
.port dw 0
|
||||
.ip dd 0
|
||||
rb 10
|
||||
|
||||
align 4
|
||||
@IMPORT:
|
||||
|
||||
library network, 'network.obj', console, 'console.obj'
|
||||
import network, \
|
||||
getaddrinfo, 'getaddrinfo', \
|
||||
freeaddrinfo, 'freeaddrinfo', \
|
||||
inet_ntoa, 'inet_ntoa'
|
||||
import console, \
|
||||
con_start, 'START', \
|
||||
con_init, 'con_init', \
|
||||
con_write_asciiz, 'con_write_asciiz', \
|
||||
con_exit, 'con_exit', \
|
||||
con_getch2, 'con_getch2', \
|
||||
con_get_flags, 'con_get_flags'
|
||||
|
||||
i_end:
|
||||
|
||||
socketnum dd ?
|
||||
socketnum2 dd ?
|
||||
start_time dd ?
|
||||
last_report dd ?
|
||||
interval_bytes dd ?
|
||||
total_bytes dd ?
|
||||
|
||||
linebuf rb 32
|
||||
cmdline rb 256
|
||||
|
||||
sendbuf rb SENDBUFSIZE
|
||||
recvbuf rb RECVBUFSIZE
|
||||
|
||||
align 4
|
||||
rb 4096 ; stack
|
||||
mem:
|
||||
Reference in new issue
Block a user