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

format MS COFF

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
           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

