linux 0.11 内核学习 -- rs_io.s,串口汇编代码

/*

 *  该文件实现rs232 串行通信中断处理

 */

/*

 *  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 = 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 */

/* 读写缓冲结构中的偏移量tty_queue */

rs_addr = 0

head = 4

tail = 8

proc_list = 12

buf = 16

/* 当写队列里还剩256 个字符空间(WAKEUP_CHARS)时,我们就可以写 */

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

/* 串行端口1 中断处理程序入口点 */

_rs1_interrupt:

// tty 表中对应串口1 的读写缓冲指针的地址入栈

pushl $_table_list+8 // table_list定义在文件tty_io.c文件中

jmp rs_int

/* 串行端口2 中断处理程序入口点 */

.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 */

/* 让ds、es 指向内核数据段 */

pushl $0x10 /* know that bs is ok. Load it */

pop %ds

pushl $0x10

pop %es

movl 24(%esp),%edx // 将缓冲队列指针地址存入edx 寄存器

movl (%edx),%edx // 取读队列指针(地址)􀃎-> edx

movl rs_addr(%edx),%edx // 取串口1 的端口号􀃎-> edx

/* edx 指向中断标识寄存器 */

addl $2,%edx /* interrupt ident. reg */

rep_int:

xorl %eax,%eax // eax = 0

inb %dx,%al // 取中断标识字节,用以判断中断来源

testb $1,%al // 有无待处理的中断?

jne end // 没有,则跳转至退出处理处end

cmpb $6,%al /* this shouldn't happen, but ... */

ja end // al 值>6? 是则跳转至end

movl 24(%esp),%ecx // 再取缓冲队列指针地址􀃎-> ecx

pushl %edx // 将端口号0x3fa(0x2fa)入栈

subl $2,%edx // 0x3f8(0x2f8)

/* jmp_table在下面定义 */

call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */

popl %edx // 弹出中断标识寄存器端口号0x3fa(或0x2fa)

jmp rep_int // 继续处理

/* 向中断控制器发送结束中断指令EOI */

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

/* 各中断类型处理程序地址跳转表 */

/* modem 状态变化中断,写字符中断,读字符中断,线路状态有问题中断 */

jmp_table:

.long modem_status,write_char,read_char,line_status

.align 2

/* 通过读modem 状态寄存器进行复位(0x3fe) */

modem_status:

addl $6,%edx /* clear intr by reading modem status reg */

inb %dx,%al

ret

.align 2

/* 通过读线路状态寄存器进行复位(0x3fd) */

line_status:

addl $5,%edx /* clear intr by reading line status reg. */

inb %dx,%al

ret

.align 2

/*

 * read_char实现的是从CPU读取,将结果存储到缓冲队列中

 */

read_char:

inb %dx,%al // 读取字符􀃎-> al

movl %ecx,%edx // 当前串口缓冲队列指针地址􀃎-> edx

subl $_table_list,%edx // 当前串口队列指针地址􀃎-> edx

shrl $3,%edx

movl (%ecx),%ecx # read-queue

// 取读队列中缓冲头指针􀃎-> ebx

movl head(%ecx),%ebx

// 将字符放在缓冲区中头指针所指的位置

movb %al,buf(%ecx,%ebx)

incl %ebx // 将头指针前移一字节

andl $size-1,%ebx // 缓冲区头指针进行模操作

cmpl tail(%ecx),%ebx // 缓冲区头指针与尾指针比较

je 1f // 若相等,表示缓冲区满,跳转到标号1 处

movl %ebx,head(%ecx) // 保存修改过的头指针

1: pushl %edx // 将串口号压入堆栈

call _do_tty_interrupt // 调用tty 中断处理C 函数

addl $4,%esp // 丢弃入栈参数,并返回

ret

.align 2

/*

 * write_char函数是将收到的数据传送给CPU

 */

write_char:

// 取写缓冲队列结构地址􀃎-> ecx

movl 4(%ecx),%ecx # write-queue

movl head(%ecx),%ebx // 取写队列头指针􀃎-> ebx

subl tail(%ecx),%ebx // 头指针 - 尾指针 = 队列中字符数

andl $size-1,%ebx # nr chars in queue

je write_buffer_empty // 如果头指针 = 尾指针,说明写队列无字符,跳转处理

cmpl $startup,%ebx // 队列中字符数超过256 个?

ja 1f // 超过,则跳转处理

// 取等待该队列的进程的指针,并判断是否为空

movl proc_list(%ecx),%ebx # wake up sleeping process

testl %ebx,%ebx # is there any?

je 1f // 是空的,则向前跳转到标号1 处

movl $0,(%ebx) // 否则将进程置为可运行状态(唤醒进程)

/*

 * 缓冲区的长度是1024,但是在缓冲区中含有256个字符时,就开始

 * 进行写操作。下面的饿代码首先将超过256的字节的后一个字节

 * 写入端口中,然后该段代码从尾部读取一个字符,然后继续rep_int

 * ,程序继续判断是那种的处理情况

 *

 * tail(low address) ---------------------> head(high address)

 */

1: movl tail(%ecx),%ebx // 取尾指针

movb buf(%ecx,%ebx),%al // 从缓冲中尾指针处取一字符􀃎-> al

outb %al,%dx // 将端口0x3f8(0x2f8)送出保存到寄存器中

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 // 无,则向前跳转到标号1 处

movl $0,(%ebx) // 唤醒进程

1: incl %edx // 指向端口0x3f9(0x2f9)

inb %dx,%al // 读取中断允许寄存器

jmp 1f // 延迟

1: jmp 1f

/* disable transmit interrupt */

1: andb $0xd,%al

outb %al,%dx

ret

参考《linux内核完全注释》和网上相关文章

posted @ 2010-02-01 15:33  qiang.xu  阅读(719)  评论(0编辑  收藏  举报