;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved.    ;;
;; Distributed under terms of the GNU General Public License       ;;
;;                                                                 ;;
;;  STACK.INC                                                      ;;
;;                                                                 ;;
;;  BASIC TCP/IP stack for KolibriOS                               ;;
;;                                                                 ;;
;;    Written by hidnplayr@kolibrios.org                           ;;
;;                                                                 ;;
;;     based on the work of Mike Hibbett, mikeh@oceanfree.net      ;;
;;     but also Paolo Franchetti                                   ;;
;;                                                                 ;;
;;          GNU GENERAL PUBLIC LICENSE                             ;;
;;             Version 2, June 1991                                ;;
;;                                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$

uglobal
	last_1sTick	db ?
	last_1hsTick	dd ?
endg

MAX_NET_DEVICES 	equ 16
QUEUE_BEFORE_SENDING	equ 1 ; 1 or 0 (enable or disable) currently only affects ethernet

MIN_EPHEMERAL_PORT equ 49152
MAX_EPHEMERAL_PORT equ 61000

ETHER		equ 1337     ; TODO: find another value for this (how does it work in posix ?)
ETHER_ARP	equ 0x0608

AF_UNSPEC	equ 0
AF_UNIX 	equ 1
AF_INET4	equ 2
;AF_AX25         equ 3
;AF_IPX          equ 4
;AF_APPLETALK    equ 5
;AF_NETROM       equ 6
;AF_BRIDGE       equ 7
;AF_AAL5         equ 8
;AF_X25          equ 9
;AF_INET6        equ 10
;AF_MAX          equ 12

IP_PROTO_IP	equ 0
IP_PROTO_ICMP	equ 1
IP_PROTO_TCP	equ 6
IP_PROTO_UDP	equ 17

; Socket types
SOCK_STREAM	= 1
SOCK_DGRAM	= 2
SOCK_RAW	= 3

TCB_LISTEN		equ 1
TCB_SYN_SENT		equ 2
TCB_SYN_RECEIVED	equ 3
TCB_ESTABLISHED 	equ 4
TCB_FIN_WAIT_1		equ 5
TCB_FIN_WAIT_2		equ 6
TCB_CLOSE_WAIT		equ 7
TCB_CLOSING		equ 8
TCB_LAST_ACK		equ 9
TCB_TIMED_WAIT		equ 10
TCB_CLOSED		equ 11

TH_FIN			equ 1 shl 0
TH_SYN			equ 1 shl 1
TH_RST			equ 1 shl 2
TH_PUSH 		equ 1 shl 3
TH_ACK			equ 1 shl 4
TH_URG			equ 1 shl 5


macro inc_INET reg {

	add	byte [reg + 3], 1
	adc	byte [reg + 2], 0
	adc	byte [reg + 1], 0
	adc	byte [reg], 0

}


macro add_INET reg {
	add	byte [reg + 3], cl
	adc	byte [reg + 2], ch
	adc	byte [reg + 1], 0
	adc	byte [reg], 0
	rol	ecx, 16
	add	byte [reg + 1], cl
	adc	byte [reg], ch
	rol	ecx, 16
}


macro pseudo_random reg {

	add	reg, [esp]
	rol	reg, 5
	xor	reg, [timer_ticks]
	imul	reg, 214013
	xor	reg, 0xdeadbeef
	rol	reg, 9

	pushd	reg
	mov	word [esp], 0x8080 ; kernel heap start addr      (os_stack)
	xor	reg, [esp]
	add	esp, 4

}

include "queue.inc"
include "ARP.inc"
include "IPv4.inc"
include "ethernet.inc"
include "socket.inc"
include "tcp.inc"
include "udp.inc"
include "icmp.inc"

;-----------------------------------------------------------------
;
; stack_init
;
;  This function calls all network init procedures
;
;  IN:  /
;  OUT: /
;
;-----------------------------------------------------------------
align 4
stack_init:

	call	ETH_init
	call	IPv4_init
	call	ARP_init
	call	UDP_init
	call	TCP_init
	call	ICMP_init
	call	socket_init

	mov	al, 0		      ; set up 1s timer
	out	0x70, al
	in	al, 0x71
	mov	[last_1sTick], al

	ret



;-----------------------------------------------------------------
;
; stack_handler
;
;  This function calls all network init procedures
;
;  IN:  /
;  OUT: /
;
;-----------------------------------------------------------------
align 4
stack_handler:

	cmp	[ETH_RUNNING], 0
	je	.exit

	; Test for 10ms tick
	mov	eax, [timer_ticks]
	cmp	eax, [last_1hsTick]
	je	.exit

	mov	[last_1hsTick], eax

	call	ETH_handler		    ; handle all queued ethernet packets
if QUEUE_BEFORE_SENDING
	call	ETH_send_queued
end if
	call	TCP_send_queued

  .sec_tick:

	; Test for 1 second tick
	mov	al, 0
	out	0x70, al
	in	al, 0x71
	cmp	al, [last_1sTick]
	je	.exit

	mov	[last_1sTick], al

	call	ARP_decrease_entry_ttls
	call	IPv4_decrease_fragment_ttls
	call	TCP_decrease_socket_ttls

  .exit:
	ret


;-----------------------------------------------------------------
;
; checksum_1
;
;  This is the first of two functions needed to calculate the TCP checksum.
;
;  IN:  edx = start offeset for semi-checksum
;       esi = pointer to data
;       ecx = data size
;  OUT: edx = semi-checksum
;
;-----------------------------------------------------------------
align 4
checksum_1:

	xor	eax, eax
	shr	ecx, 1
	pushf
.loop:
	lodsw
	xchg	al, ah
	add	edx, eax
	loop	.loop

	popf
	jnc	.end

	add	dh, [esi]
	adc	edx, 0

.end:

	ret


;-----------------------------------------------------------------
;
; checksum_2
;
;  This function calculates the final ip/tcp/udp checksum for you
;
;  IN:  edx = semi-checksum
;  OUT: dx = checksum (in INET byte order)
;
;-----------------------------------------------------------------
align 4
checksum_2:

	mov	ecx, edx
	shr	ecx, 16
	and	edx, 0xffff
	add	edx, ecx
	mov	eax, edx
	shr	eax, 16
	add	edx, eax

	not	dx
	jnz	.not_zero
	dec	dx
  .not_zero:
	xchg	dl, dh

	DEBUGF 1,"Checksum: %x\n",dx

	ret



;----------------------------------------------------------------
;
;  System function to work with network devices (73)
;
;----------------------------------------------------------------
align 4
sys_network:

	cmp	ebx, -1
	jne	@f

	mov	eax, [ETH_RUNNING]
	jmp	.return

   @@:
	cmp	bh, MAX_NET_DEVICES		 ; Check if device number exists
	jge	.doesnt_exist

	mov	esi, ebx
	and	esi, 0x0000ff00
	shr	esi, 6

	cmp	dword [esi + ETH_DRV_LIST], 0 ; check if driver is running
	je	.doesnt_exist

	test	bl, bl			; 0 = Get device type (ethernet/token ring/...)
	jnz	@f
					 ; todo
	xor	eax, eax
	jmp	.return


  @@:
	dec	bl			; 1 = Get device name
	jnz	@f

	mov	esi, [esi + ETH_DRV_LIST]
	mov	esi, [esi + ETH_DEVICE.name]
	mov	edi, ecx

	mov	ecx, 64 ; max length
	repnz	movsb

	xor	eax, eax
	jmp	.return

  @@:

	dec	bl			; 2 = Reset the device
	jnz	@f

	mov	esi, [esi + ETH_DRV_LIST]
	call	[esi + ETH_DEVICE.reset]
	jmp	.return

  @@:

	dec	bl			; 3 = Stop driver for this device
	jnz	@f

	mov	esi, [esi + ETH_DRV_LIST]
	call	[esi + ETH_DEVICE.unload]
	jmp	.return

  @@:
	dec	bl			; 4 = Get driver pointer
	jnz	@f

	; ..;


  @@:
;  ...                                   ; 5 Get driver name

  .doesnt_exist:
	DEBUGF	1,"sys_network: invalid device/function specified!\n"
	mov	eax, -1

  .return:
	mov	[esp+28+4], eax
	ret


;----------------------------------------------------------------
;
;  System Function To work with Protocols  (75)
;
;----------------------------------------------------------------
align 4
sys_protocols:
	cmp	bh, MAX_NET_DEVICES		; Check if device number exists
	jge	.doesnt_exist

	mov	esi, ebx
	and	esi, 0x0000ff00
	shr	esi, 6
	cmp	dword [esi + ETH_DRV_LIST], 0	; check if driver is running TODO: check other lists too
	je	.doesnt_exist

	push	.return 			; return address (we will be using jumps instead of calls)

	mov	eax, ebx			; set ax to protocol number
	shr	eax, 16 			;

	cmp	ax , IP_PROTO_IP
	je	IPv4_API

	cmp	ax , IP_PROTO_ICMP
	je	ICMP_API

	cmp	ax , IP_PROTO_UDP
	je	UDP_API

	cmp	ax , IP_PROTO_TCP
	je	TCP_API

	cmp	ax , ETHER_ARP
	je	ARP_API

	cmp	ax , ETHER
	je	ETH_API

	add	esp, 4				 ; if we reached here, no function was called, so we need to balance stack

  .doesnt_exist:
	DEBUGF	1,"sys_protocols: protocol %u doesnt exist on device %u!\n",ax, bh
	mov	eax, -1

  .return:
	mov	[esp+28+4], eax
	ret
