;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

format MS COFF

DEBUG           equ 1

include 'proc32.inc'
include 'imports.inc'



API_VERSION	  equ 0
UART_VERSION	  equ API_VERSION

PG_SW		  equ 0x003
page_tabs	  equ 0xFDC00000     ;hack

OS_BASE 	  equ 0x80000000
SLOT_BASE	  equ (OS_BASE+0x0080000)
TASK_COUNT	  equ (OS_BASE+0x0003004)
CURRENT_TASK	  equ (OS_BASE+0x0003000)


struc APPOBJ	       ;common object header
{
   .magic	dd ?   ;
   .destroy	dd ?   ;internal destructor
   .fd		dd ?   ;next object in list
   .bk		dd ?   ;prev object in list
   .pid 	dd ?   ;owner id
};

virtual at 0
  APPOBJ APPOBJ
end virtual

struc IOCTL
{  .handle	dd ?
   .io_code	dd ?
   .input	dd ?
   .inp_size	dd ?
   .output	dd ?
   .out_size	dd ?
}

virtual at 0
  IOCTL IOCTL
end virtual

DEBUG		 equ   1

DRV_ENTRY	 equ   1
DRV_EXIT	 equ  -1

THR_REG 	 equ  0;  x3f8   ;transtitter/reciever
IER_REG 	 equ  1;  x3f9   ;interrupt enable
IIR_REG 	 equ  2;  x3fA   ;interrupt info
LCR_REG 	 equ  3;  x3FB   ;line control
MCR_REG 	 equ  4;  x3FC   ;modem control
LSR_REG 	 equ  5;  x3FD   ;line status
MSR_REG 	 equ  6;  x3FE   ;modem status

LCR_5BIT	 equ  0x00
LCR_6BIT	 equ  0x01
LCR_7BIT	 equ  0x02
LCR_8BIT	 equ  0x03
LCR_STOP_1	 equ  0x00
LCR_STOP_2	 equ  0x04
LCR_PARITY	 equ  0x08
LCR_EVEN	 equ  0x10
LCR_STICK	 equ  0x20
LCR_BREAK	 equ  0x40
LCR_DLAB	 equ  0x80

LSR_DR		 equ  0x01     ;data ready
LSR_OE		 equ  0x02     ;overrun error
LSR_PE		 equ  0x04     ;parity error
LSR_FE		 equ  0x08     ;framing error
LSR_BI		 equ  0x10     ;break interrupt
LSR_THRE	 equ  0x20     ;transmitter holding empty
LSR_TEMT	 equ  0x40     ;transmitter empty
LSR_FER 	 equ  0x80     ;FIFO error

FCR_EFIFO	 equ  0x01     ;enable FIFO
FCR_CRB 	 equ  0x02     ;clear reciever FIFO
FCR_CXMIT	 equ  0x04     ;clear transmitter FIFO
FCR_RDY 	 equ  0x08     ;set RXRDY and TXRDY pins
FCR_FIFO_1	 equ  0x00     ;1  byte trigger
FCR_FIFO_4	 equ  0x40     ;4  bytes trigger
FCR_FIFO_8	 equ  0x80     ;8  bytes trigger
FCR_FIFO_14	 equ  0xC0     ;14 bytes trigger

IIR_INTR	 equ  0x01     ;1= no interrupts

IER_RDAI	 equ  0x01     ;reciever data interrupt
IER_THRI	 equ  0x02     ;transmitter empty interrupt
IER_LSI 	 equ  0x04     ;line status interrupt
IER_MSI 	 equ  0x08     ;modem status interrupt

MCR_DTR 	 equ  0x01     ;0-> DTR=1, 1-> DTR=0
MCR_RTS 	 equ  0x02     ;0-> RTS=1, 1-> RTS=0
MCR_OUT_1	 equ  0x04     ;0-> OUT1=1, 1-> OUT1=0
MCR_OUT_2	 equ  0x08     ;0-> OUT2=1, 1-> OUT2=0;  enable intr
MCR_LOOP	 equ  0x10     ;lopback mode

MSR_DCTS	 equ  0x01     ;delta clear to send
MSR_DDSR	 equ  0x02     ;delta data set redy
MSR_TERI	 equ  0x04     ;trailinh edge of ring
MSR_DDCD	 equ  0x08     ;delta carrier detect


RATE_50 	 equ  0
RATE_75 	 equ  1
RATE_110	 equ  2
RATE_134	 equ  3
RATE_150	 equ  4
RATE_300	 equ  5
RATE_600	 equ  6
RATE_1200	 equ  7
RATE_1800	 equ  8
RATE_2000	 equ  9
RATE_2400	 equ 10
RATE_3600	 equ 11
RATE_4800	 equ 12
RATE_7200	 equ 13
RATE_9600	 equ 14
RATE_19200	 equ 15
RATE_38400	 equ 16
RATE_57600	 equ 17
RATE_115200	 equ 18

COM_1		 equ  1
COM_2		 equ  2
COM_3		 equ  3
COM_4		 equ  4
COM_MAX 	 equ  2    ;only two port supported

COM_1_BASE	 equ 0x3F8
COM_2_BASE	 equ 0x2F8

COM_1_IRQ	 equ  4
COM_2_IRQ	 equ  3

UART_CLOSED	 equ  0
UART_TRANSMIT	 equ  1
UART_STOP	 equ  2

struc UART
{
   .lock	 dd ?
   .base	 dd ?
   .lcr_reg	 dd ?
   .mcr_reg	 dd ?
   .rate	 dd ?
   .mode	 dd ?
   .state	 dd ?

   .rcvr_buff	 dd ?
   .rcvr_rp	 dd ?
   .rcvr_wp	 dd ?
   .rcvr_count	 dd ?
   .rcvr_top	 dd ?

   .xmit_buff	 dd ?
   .xmit_rp	 dd ?
   .xmit_wp	 dd ?
   .xmit_count	 dd ?
   .xmit_free	 dd ?
   .xmit_top	 dd ?
}
virtual at 0
  UART UART
end virtual

UART_SIZE     equ 18*4

struc CONNECTION
{
   .magic	dd ?   ;'CNCT'
   .destroy	dd ?   ;internal destructor
   .fd		dd ?   ;next object in list
   .bk		dd ?   ;prev object in list
   .pid 	dd ?   ;owner id

   .id		dd ?   ;reserved
   .uart	dd ?   ;uart pointer
}

virtual at 0
  CONNECTION CONNECTION
end virtual

CONNECTION_SIZE equ 7*4

public START
public service_proc
public version

section '.flat' code readable align 16

proc START stdcall, state:dword

	   cmp [state], 1
	   jne .stop

	   mov eax, UART_SIZE
	   call Kmalloc
	   test eax, eax
	   jz .fail

	   mov [com1], eax
	   mov edi, eax
	   mov ecx, UART_SIZE/4
	   xor eax, eax
	   cld
	   rep stosd

	   mov eax, [com1]
	   mov [eax+UART.base], COM_1_BASE

	   stdcall AllocKernelSpace, 32768

	   mov edi, [com1]
	   mov edx, eax

	   mov [edi+UART.rcvr_buff], eax
	   add eax, 8192
	   mov [edi+UART.rcvr_top], eax
	   add eax, 8192
	   mov [edi+UART.xmit_buff], eax
	   add eax, 8192
	   mov [edi+UART.xmit_top], eax

	   call AllocPage
	   test eax, eax
	   jz .fail

	   shr edx, 12
	   or eax, PG_SW
	   mov [page_tabs+edx*4], eax
	   mov [page_tabs+edx*4+8], eax

	   call AllocPage
	   test eax, eax
	   jz .fail

	   or eax, PG_SW
	   mov [page_tabs+edx*4+4], eax
	   mov [page_tabs+edx*4+12], eax

	   call AllocPage
	   test eax, eax
	   jz .fail

	   or eax, PG_SW
	   mov [page_tabs+edx*4+16], eax
	   mov [page_tabs+edx*4+24], eax

	   call AllocPage
	   test eax, eax
	   jz .fail

	   or eax, PG_SW
	   mov [page_tabs+edx*4+20], eax
	   mov [page_tabs+edx*4+28], eax

	   mov eax, [edi+UART.rcvr_buff]
	   invlpg [eax]
	   invlpg [eax+0x1000]
	   invlpg [eax+0x2000]
	   invlpg [eax+0x3000]
	   invlpg [eax+0x4000]
	   invlpg [eax+0x5000]
	   invlpg [eax+0x6000]
	   invlpg [eax+0x7000]

	   mov eax, edi
	   call uart_reset.internal   ;eax= uart

	   stdcall AttachIntHandler, COM_1_IRQ, com_1_isr, dword 0
	   stdcall RegService, sz_uart_srv, service_proc
	   ret
.fail:
.stop:
	   xor eax, eax
	   ret
endp


handle	   equ	IOCTL.handle
io_code    equ	IOCTL.io_code
input	   equ	IOCTL.input
inp_size   equ	IOCTL.inp_size
output	   equ	IOCTL.output
out_size   equ	IOCTL.out_size

SRV_GETVERSION	equ 0
PORT_OPEN	equ 1
PORT_CLOSE	equ 2
PORT_RESET	equ 3
PORT_SETMODE	equ 4
PORT_GETMODE	equ 5
PORT_SETMCR	equ 6
PORT_GETMCR	equ 7
PORT_READ	equ 8
PORT_WRITE	equ 9

align 4
proc service_proc stdcall, ioctl:dword

	   mov ebx, [ioctl]
	   mov eax, [ebx+io_code]
	   cmp eax, PORT_WRITE
	   ja .fail

	   cmp eax, SRV_GETVERSION
	   jne @F

	   mov eax, [ebx+output]
	   cmp [ebx+out_size], 4
	   jne .fail
	   mov [eax], dword UART_VERSION
	   xor eax, eax
	   ret
@@:
	   cmp eax, PORT_OPEN
	   jne @F

	   cmp [ebx+out_size], 4
	   jne .fail

	   mov ebx, [ebx+input]
	   mov eax, [ebx]
	   call uart_open
	   mov ebx, [ioctl]
	   mov ebx, [ebx+output]
	   mov [ebx], ecx
	   ret
@@:
	   mov esi, [ebx+input]     ;input buffer
	   mov edi, [ebx+output]
	   call [uart_func+eax*4]
	   ret
.fail:
	   or eax, -1
	   ret

endp

restore   handle
restore   io_code
restore   input
restore   inp_size
restore   output
restore   out_size


; param
;  esi=  input buffer
;        +0 connection
;
; retval
;  eax= error code

align 4
uart_reset:
	   mov eax, [esi]
	   cmp [eax+APPOBJ.magic], 'CNCT'
	   jne .fail

	   cmp [eax+APPOBJ.destroy], uart_close.destroy
	   jne .fail

	   mov eax, [eax+CONNECTION.uart]
	   test eax, eax
	   jz .fail

; set mode 2400 bod 8-bit
; disable DTR & RTS
; clear FIFO
; clear pending interrupts
;
; param
;  eax= uart

align 4
.internal:
	   mov esi, eax
	   mov [eax+UART.state], UART_CLOSED
	   mov edx, [eax+UART.base]
	   add edx, MCR_REG
	   xor eax, eax
	   out dx, al	     ;clear DTR & RTS

	   mov eax, esi
	   mov ebx, RATE_2400
	   mov ecx, LCR_8BIT+LCR_STOP_1
	   call uart_set_mode.internal

	   mov edx, [esi+UART.base]
	   add edx, IIR_REG
	   mov eax,FCR_EFIFO+FCR_CRB+FCR_CXMIT+FCR_FIFO_14
	   out dx, al
.clear_RB:
	   mov edx, [esi+UART.base]
	   add edx, LSR_REG
	   in al, dx
	   test eax, LSR_DR
	   jz @F

	   mov edx, [esi+UART.base]
	   in al, dx
	   jmp .clear_RB
@@:
	   mov edx, [esi+UART.base]
	   add edx, IER_REG
	   mov eax,IER_RDAI+IER_THRI+IER_LSI
	   out dx, al
.clear_IIR:
	   mov edx, [esi+UART.base]
	   add edx, IIR_REG
	   in al, dx
	   test al, IIR_INTR
	   jnz .done

	   shr eax, 1
	   and eax, 3
	   jnz @F

	   mov edx, [esi+UART.base]
	   add edx, MSR_REG
	   in al, dx
	   jmp .clear_IIR
@@:
	   cmp eax, 1
	   je .clear_IIR

	   cmp eax, 2
	   jne @F

	   mov edx, [esi+UART.base]
	   in al, dx
	   jmp .clear_IIR
@@:
	   mov edx, [esi+UART.base]
	   add edx, LSR_REG
	   in al, dx
	   jmp .clear_IIR
.done:
	   mov edi, [esi+UART.rcvr_buff]
	   mov ecx, 8192/4
	   xor eax, eax

	   mov [esi+UART.rcvr_rp], edi
	   mov [esi+UART.rcvr_wp], edi
	   mov [esi+UART.rcvr_count], eax

	   cld
	   rep stosd

	   mov edi, [esi+UART.xmit_buff]
	   mov ecx, 8192/4

	   mov [esi+UART.xmit_rp], edi
	   mov [esi+UART.xmit_wp], edi
	   mov [esi+UART.xmit_count], eax
	   mov [esi+UART.xmit_free], 8192

	   rep stosd
	   ret		     ;eax= 0
.fail:
	   or eax, -1
	   ret

; param
;  esi=  input buffer
;        +0 connection
;        +4 rate
;        +8 mode
;
; retval
;  eax= error code

align 4
uart_set_mode:
	   mov eax, [esi]
	   cmp [eax+APPOBJ.magic], 'CNCT'
	   jne .fail

	   cmp [eax+APPOBJ.destroy], uart_close.destroy
	   jne .fail

	   mov eax, [eax+CONNECTION.uart]
	   test eax, eax
	   jz .fail

	   mov ebx, [esi+4]
	   mov ecx, [esi+8]

; param
;  eax= uart
;  ebx= baud rate
;  ecx= mode

align 4
.internal:
	   cmp ebx, RATE_115200
	   ja .fail

	   cmp ecx, LCR_BREAK
	   jae .fail

	   mov [eax+UART.rate], ebx
	   mov [eax+UART.mode], ecx

	   mov esi, eax
	   mov bx, [divisor+ebx*2]

	   mov edx, [esi+UART.base]
	   push edx
	   add edx, LCR_REG
	   in al, dx
	   or al, 0x80
	   out dx, al

	   pop edx
	   mov al, bl
	   out dx, al

	   inc dx
	   mov al, bh
	   out dx, al

	   add edx, LCR_REG-1
	   mov eax, ecx
	   out dx, al
	   xor eax, eax
	   ret
.fail:
	   or eax, -1
	   ret

; param
;  esi=  input buffer
;        +0 connection
;        +4 modem control reg valie
;
; retval
;  eax= error code

align 4
uart_set_mcr:

	   mov eax, [esi]
	   cmp [eax+APPOBJ.magic], 'CNCT'
	   jne .fail

	   cmp [eax+APPOBJ.destroy], uart_close.destroy
	   jne .fail

	   mov eax, [eax+CONNECTION.uart]
	   test eax, eax
	   jz .fail

	   mov ebx, [esi+4]

	   mov [eax+UART.mcr_reg], ebx
	   mov edx, [eax+UART.base]
	   add edx, MCR_REG
	   mov al, bl
	   out dx, al
	   xor eax, eax
	   ret
.fail:
	   or eax, -1
	   ret

; param
;  eax= port
;
; retval
;  ecx= connection
;  eax= error code

align 4
uart_open:
	   dec eax
	   cmp eax, COM_MAX
	   jae .fail

	   mov esi, [com1+eax*4]	     ;uart
	   push esi
.do_wait:
	   cmp dword [esi+UART.lock],0
	   je .get_lock
      ;     call change_task
	   jmp .do_wait
.get_lock:
	   mov eax, 1
	   xchg eax, [esi+UART.lock]
	   test eax, eax
	   jnz .do_wait

	   mov eax, esi 		 ;uart
	   call uart_reset.internal

	   mov ebx, [CURRENT_TASK]
	   shl ebx, 5
	   mov ebx, [CURRENT_TASK+ebx+4]
	   mov eax, CONNECTION_SIZE
	   call CreateObject
	   pop esi			 ;uart
	   test eax, eax
	   jz .fail

	   mov [eax+APPOBJ.magic], 'CNCT'
	   mov [eax+APPOBJ.destroy], uart_close.destroy
	   mov [eax+CONNECTION.uart], esi
	   mov ecx, eax
	   xor eax, eax
	   ret
.fail:
	   or eax, -1
	   ret
restore .uart

; param
;  esi= input buffer

align 4
uart_close:
	   mov eax, [esi]
	   cmp [eax+APPOBJ.magic], 'CNCT'
	   jne .fail

	   cmp [eax+APPOBJ.destroy], uart_close.destroy
	   jne .fail
.destroy:
	   push [eax+CONNECTION.uart]
	   call DestroyObject	;eax= object
	   pop eax			;eax= uart
	   test eax, eax
	   jz .fail

	   mov [eax+UART.state], UART_CLOSED
	   mov [eax+UART.lock], 0 ;release port
	   xor eax, eax
	   ret
.fail:
	   or eax, -1
	   ret


; param
;  eax= uart
;  ebx= baud rate

align 4
set_rate:
	   cmp ebx, RATE_115200
	   ja .fail

	   mov [eax+UART.rate], ebx
	   mov bx, [divisor+ebx*2]

	   mov edx, [eax+UART.base]
	   add edx, LCR_REG
	   in al, dx
	   push eax
	   or al, 0x80
	   out dx, al

	   sub edx, LCR_REG
	   mov al, bl
	   out dx, al

	   inc edx
	   mov al, bh
	   out dx, al

	   pop eax
	   add edx, LCR_REG-1
	   out dx, al
.fail:
	   ret


; param
;   ebx= uart

align 4
transmit:
	   push esi
	   push edi

	   mov edx, [ebx+UART.base]

	   pushfd
	   cli

	   mov esi, [ebx+UART.xmit_rp]
	   mov ecx, [ebx+UART.xmit_count]
	   test ecx, ecx
	   je .stop

	   cmp ecx, 16
	   jbe @F
	   mov ecx, 16
@@:
	   sub [ebx+UART.xmit_count], ecx
	   add [ebx+UART.xmit_free], ecx
	   cld
@@:
	   lodsb
	   out dx, al
	   dec ecx
	   jnz @B

	   cmp esi,[ebx+UART.xmit_top]
	   jb @F
	   sub esi, 8192
@@:
	   mov [ebx+UART.xmit_rp], esi

	   cmp [ebx+UART.xmit_count], 0
	   je .stop

	   mov [ebx+UART.state], UART_TRANSMIT
	   jmp @F
.stop:
	   mov [ebx+UART.state], UART_STOP
@@:
	   popfd
	   pop edi
	   pop esi
	   ret


; param
;  esi=  input buffer
;        +0 connection
;        +4 dst buffer
;        +8 dst size
;  edi=  output buffer
;        +0 bytes read

; retval
;  eax= error code

align 4
uart_read:
	   mov eax, [esi]
	   cmp [eax+APPOBJ.magic], 'CNCT'
	   jne .fail

	   cmp [eax+APPOBJ.destroy], uart_close.destroy
	   jne .fail

	   mov eax, [eax+CONNECTION.uart]
	   test eax, eax
	   jz .fail

	   mov ebx, [esi+8]    ;dst size
	   mov ecx, [eax+UART.rcvr_count]
	   cmp ecx, ebx
	   jbe @F
	   mov ecx, ebx
@@:
	   mov [edi], ecx      ;bytes read
	   test ecx, ecx
	   jz .done

	   push ecx

	   mov edi, [esi+4]    ;dst
	   mov esi, [eax+UART.rcvr_rp]
	   cld
	   rep movsb
	   pop ecx

	   cmp esi, [eax+UART.rcvr_top]
	   jb @F
	   sub esi, 8192
@@:
	   mov [eax+UART.rcvr_rp], esi
	   sub [eax+UART.rcvr_count], ecx
.done:
	   xor eax, eax
	   ret
.fail:
	   or eax, -1
	   ret

; param
;  esi=  input buffer
;        +0 connection
;        +4 src buffer
;        +8 src size
;
; retval
;  eax= error code

align 4
uart_write:
	   mov eax, [esi]
	   cmp [eax+APPOBJ.magic], 'CNCT'
	   jne .fail

	   cmp [eax+APPOBJ.destroy], uart_close.destroy
	   jne .fail

	   mov eax, [eax+CONNECTION.uart]
	   test eax, eax
	   jz .fail

	   mov ebx, [esi+4]
	   mov edx, [esi+8]

; param
;  eax= uart
;  ebx= src
;  edx= count

align 4
.internal:
	   mov esi, ebx
	   mov edi, [eax+UART.xmit_wp]
.write:
	   test edx, edx
	   jz .fail
.wait:
	   cmp [eax+UART.xmit_free], 0
	   jne .fill

	   cmp [eax+UART.state], UART_TRANSMIT
	   je .wait

	   mov ebx, eax
	   push edx
	   call transmit
	   pop edx
	   mov eax, ebx
	   jmp .write
.fill:
	   mov ecx, [eax+UART.xmit_free]
	   cmp ecx, edx
	   jbe @F
	   mov ecx, edx
@@:
	   push ecx
	   cld
	   rep movsb
	   pop ecx
	   sub [eax+UART.xmit_free], ecx
	   add [eax+UART.xmit_count], ecx
	   sub edx, ecx
	   jnz .wait
.done:
	   cmp edi, [eax+UART.xmit_top]
	   jb @F
	   sub edi, 8192
@@:
	   mov [eax+UART.xmit_wp], edi
	   cmp [eax+UART.state], UART_TRANSMIT
	   je @F
	   mov ebx, eax
	   call transmit
@@:
	   xor eax, eax
	   ret
.fail:
	   or eax, -1
	   ret


align 4
com_2_isr:
	   mov ebx, [com2]
	   jmp com_1_isr.get_info
align 4
com_1_isr:
	   mov ebx, [com1]
.get_info:
	   mov edx, [ebx+UART.base]
	   add edx, IIR_REG
	   in  al, dx

	   test al, IIR_INTR
	   jnz .done

	   shr eax, 1
	   and eax, 3

	   call [isr_action+eax*4]
	   jmp .get_info
.done:
	   ret

align 4
isr_line:
	   mov edx, [ebx+UART.base]
	   add edx, LSR_REG
	   in al, dx
	   ret

align 4
isr_recieve:
	   mov esi, [ebx+UART.base]
	   add esi, LSR_REG
	   mov edi, [ebx+UART.rcvr_wp]
	   xor ecx, ecx
	   cld
.read:
	   mov edx, esi
	   in al, dx
	   test eax, LSR_DR
	   jz .done

	   mov edx, [ebx+UART.base]
	   in al, dx
	   stosb
	   inc ecx
	   jmp .read
.done:
	   cmp edi, [ebx+UART.rcvr_top]
	   jb @F
	   sub edi, 8192
@@:
	   mov [ebx+UART.rcvr_wp], edi
	   add [ebx+UART.rcvr_count], ecx
	   ret

align 4
isr_modem:
	   mov edx, [ebx+UART.base]
	   add edx, MSR_REG
	   in al, dx
	   ret


align 4
divisor    dw 2304, 1536, 1047, 857, 768, 384
	   dw  192,   96,   64,  58,  48,  32
	   dw	24,   16,   12,   6,   3,   2, 1

align 4
uart_func   dd 0		;SRV_GETVERSION
	    dd 0		;PORT_OPEN
	    dd uart_close	;PORT_CLOSE
	    dd uart_reset	;PORT_RESET
	    dd uart_set_mode	;PORT_SETMODE
	    dd 0		;PORT_GETMODE
	    dd uart_set_mcr	;PORT_SETMODEM
	    dd 0		;PORT_GETMODEM
	    dd uart_read	;PORT_READ
	    dd uart_write	;PORT_WRITE

isr_action  dd isr_modem
	    dd transmit
	    dd isr_recieve
	    dd isr_line

version     dd (5 shl 16) or (UART_VERSION and 0xFFFF)

sz_uart_srv db 'UART',0

align 4

com1	    rd 1
com2	    rd 1