aboutsummaryrefslogtreecommitdiff
path: root/linux/kernel/rs_io.s
diff options
context:
space:
mode:
Diffstat (limited to 'linux/kernel/rs_io.s')
-rw-r--r--linux/kernel/rs_io.s141
1 files changed, 141 insertions, 0 deletions
diff --git a/linux/kernel/rs_io.s b/linux/kernel/rs_io.s
new file mode 100644
index 0000000..62f075f
--- /dev/null
+++ b/linux/kernel/rs_io.s
@@ -0,0 +1,141 @@
+/*
+ * rs_io.s
+ *
+ * This module implements the rs232 io interrupts.
+ */
+
+.text
+.globl _rs1_interrupt,_rs2_interrupt
+
+size = 1024 /* 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.
+ */
+.align 2
+_rs1_interrupt:
+ pushl $_table_list+8
+ jmp rs_int
+.align 2
+_rs2_interrupt:
+ pushl $_table_list+16
+rs_int:
+ 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
+ movl (%edx),%edx
+ movl rs_addr(%edx),%edx
+ addl $2,%edx /* interrupt ident. reg */
+rep_int:
+ xorl %eax,%eax
+ inb %dx,%al
+ testb $1,%al
+ jne end
+ cmpb $6,%al /* this shouldn't happen, but ... */
+ ja end
+ movl 24(%esp),%ecx
+ pushl %edx
+ subl $2,%edx
+ call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */
+ popl %edx
+ jmp rep_int
+end: movb $0x20,%al
+ outb %al,$0x20 /* EOI */
+ pop %ds
+ pop %es
+ popl %eax
+ popl %ebx
+ popl %ecx
+ popl %edx
+ addl $4,%esp # jump over _table_list entry
+ iret
+
+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)
+ pushl %edx
+ call _do_tty_interrupt
+ addl $4,%esp
+1: ret
+
+.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