/*
 *  linux/kernel/rs_io.s
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 *	rs_io.s
 *
 * This module implements the rs232 io interrupts.
 */

.text
.globl _rs1_interrupt,_rs2_interrupt

size	= 2048				/* must be power of two !
					   and must match the value
					   in tty_io.c!!! */

/* these are the offsets into the read/write buffer structures */
rs_addr = 0
head = 4
tail = 8
proc_list = 12
buf = 16

startup	= 256		/* chars left in write queue when we restart it */

/*
 * These are the actual interrupt routines. They look where
 * the interrupt is coming from, and take appropriate action.
 *
 * rs1_interrupt (IRQ 4) takes care of com1 and com3
 * rs2_interrupt (IRQ 3) takes care of com2 and com4
 */
.align 2
_rs1_interrupt:
	pushl $_table_list+8
	pushl $_table_list+24
	jmp rs_int

.align 2
_rs2_interrupt:
	pushl $_table_list+16
	pushl $_table_list+32
rs_int:	cld
	pushl %edx
	pushl %ecx
	pushl %ebx
	pushl %eax
	push %es
	push %ds		/* as this is an interrupt, we cannot */
	pushl $0x10		/* know that bs is ok. Load it */
	pop %ds
	pushl $0x10
	pop %es
	movl 24(%esp),%edx
	call do_rs_intr
	movl 28(%esp),%edx
	call do_rs_intr
	movb $0x20,%al
	outb %al,$0x20		/* EOI */
	pop %ds
	pop %es
	popl %eax
	popl %ebx
	popl %ecx
	popl %edx
	addl $8,%esp		# jump over the _table_list entries
	iret

do_rs_intr:
	pushl %edx
	movl (%edx),%edx
	movl rs_addr(%edx),%edx
	addl $2,%edx		/* interrupt ident. reg */
1:	xorl %eax,%eax
	inb %dx,%al
	testb $1,%al
	jne 2f
	cmpb $6,%al		/* this shouldn't happen, but ... */
	ja 2f
	movl (%esp),%ecx
	pushl %edx
	subl $2,%edx
	call jmp_table(,%eax,2)		/* NOTE! not *4, bit0 is 0 already */
	popl %edx
	jmp 1b
2:	popl %edx
	ret

jmp_table:
	.long modem_status,write_char,read_char,line_status

.align 2
modem_status:
	addl $6,%edx		/* clear intr by reading modem status reg */
	inb %dx,%al
	ret

.align 2
line_status:
	addl $5,%edx		/* clear intr by reading line status reg. */
	inb %dx,%al
	ret

.align 2
read_char:
	inb %dx,%al
	movl %ecx,%edx
	subl $_table_list,%edx
	shrl $3,%edx
	movl (%ecx),%ecx		# read-queue
	movl head(%ecx),%ebx
	movb %al,buf(%ecx,%ebx)
	incl %ebx
	andl $size-1,%ebx
	cmpl tail(%ecx),%ebx
	je 1f
	movl %ebx,head(%ecx)
1:	movl mask_table(,%edx,4),%edx
	orl %edx,_timer_active
	ret

.align 2
mask_table:
	.long 0,4,8,16,32

.align 2
write_char:
	movl 4(%ecx),%ecx		# write-queue
	movl head(%ecx),%ebx
	subl tail(%ecx),%ebx
	andl $size-1,%ebx		# nr chars in queue
	je write_buffer_empty
	cmpl $startup,%ebx
	ja 1f
	movl proc_list(%ecx),%ebx	# wake up sleeping process
	testl %ebx,%ebx			# is there any?
	je 1f
	movl $0,(%ebx)
1:	movl tail(%ecx),%ebx
	movb buf(%ecx,%ebx),%al
	outb %al,%dx
	incl %ebx
	andl $size-1,%ebx
	movl %ebx,tail(%ecx)
	cmpl head(%ecx),%ebx
	je write_buffer_empty
	ret

.align 2
write_buffer_empty:
	movl proc_list(%ecx),%ebx	# wake up sleeping process
	testl %ebx,%ebx			# is there any?
	je 1f
	movl $0,(%ebx)
1:	incl %edx
	inb %dx,%al
	jmp 1f
1:	jmp 1f
1:	andb $0xd,%al			# disable transmit interrupt
	outb %al,%dx
	ret
