include\linux下添加一个tty.h,我们定义了一个队列结构体tty_queue,定义了一个tty_struct结构体,里面有三个队列read_q、write_q、secondary还有一个函数指针,剩下的就是一些对队列的操作。
#ifndef TTY_H
#define TTY_H
#define TTY_BUF_SIZE 1024
struct tty_queue {
unsigned long data;
unsigned long head;
unsigned long tail;
struct task_struct * proc_list;
char buf[TTY_BUF_SIZE];
};
struct tty_struct {
void (*write)(struct tty_struct * tty);
struct tty_queue read_q;
struct tty_queue write_q;
struct tty_queue secondary;
};
#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
#define EMPTY(a) ((a).head == (a).tail)
#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1))
#define GETCH(queue,c) \
(void)({c=(queue).buf[(queue).tail];INC((queue).tail);})
#define PUTCH(c,queue) \
(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})
extern struct tty_struct tty_table[];
#endif
key_board.c中定义一个tty_table数组,类型是tty_struct,其实在这里直接用一个tty_struct类型的变量会好一些,不过为了和linux统一也为了以后扩展方便就定义了一个数组。
put_queue是将扫描码如队列
#include <linux/tty.h>
extern void con_write(struct tty_struct * tty);
struct tty_struct tty_table[] = {
{
con_write,
{0,0,0,0,""}, /* console read-queue */
{0,0,0,0,""}, /* console write-queue */
{0,0,0,0,""} /* console secondary queue */
}
};
void put_queue(char scan_code)
{
unsigned long head;
head=tty_table[0].read_q.head;
tty_table[0].read_q.buf[head] = scan_code;
head++;
if(head>TTY_BUF_SIZE-1)
head = 0;
if(head != tty_table[0].read_q.tail)//如果头指针不等于尾指针
tty_table[0].read_q.head=head;
}
console.c的改动比较大,但是很简单,首先我们先定义了一堆和屏幕、显存相关的变量,然后在con_init中对这些变量初始化。con_write函数的主要工作是解析写队列中的扫描码,转化为相应的字符并显示在屏幕上,当然目前的con_write并不完善。do_tty_interrupt中调用了copy_to_cooked,copy_to_cooked将读队列中的扫描码放到写队列中,然后调用tty->write(tty)
#include <linux/head.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/tty.h>
static unsigned char video_type; //使用的显示类型
static unsigned long video_num_columns; //屏幕文本列数
static unsigned long video_num_lines; //屏幕文本行数
static unsigned long video_size_row; //屏幕每行使用的字节数
static unsigned long video_mem_start; //显示内存起始地址
static unsigned long video_mem_end; //显示内存结束地址
static unsigned short video_port_reg; //显示控制索引寄存器端口
static unsigned short video_port_val; //显示控制数据寄存器端口
static unsigned short video_erase_char; //擦除字符属性及字符(0x0720)
static unsigned long origin; //一屏的起始内存地址
static unsigned long scr_end; //一屏的末端内存地址
static unsigned long pos; //当前光标对应显示内存的位置
static unsigned long x,y; //当前光标的位置
static unsigned long top,bottom;//滚动时顶行行号和底行行号
static unsigned char attr=0x0f;//字符属性(黑底白字,字符高亮)高四位:背景(高亮/R/G/B);低四位:前景(高亮/R/G/B)
#define VIDEO_TYPE_EGAC 0x21 // EGA/VGA 彩色模式
#define ORIG_X 0//初始光标列号
#define ORIG_Y 0//初始光标行号
#define ORIG_VIDEO_COLS (80)//字符列数
#define ORIG_VIDEO_LINES (25)//字符行数
extern void keyboard_interrupt(void);
//设置光标的位置变量x,y和光标在显存中对应的位置pos
static inline void gotoxy(unsigned int new_x,unsigned int new_y)
{
//如果给定光标超出屏幕范围,则退出
if (new_x > video_num_columns || new_y >= video_num_lines)
return;
x=new_x;
y=new_y;
//origin是一屏的起始内存地址,video_size_row是每行使用的字节数,1列用两个字节表示所以x<<1
pos=origin + y*video_size_row + (x<<1);
}
void con_init(void)
{
register unsigned char a;
char *display_ptr;
video_num_columns = ORIG_VIDEO_COLS;//字符列数,80
video_size_row = video_num_columns * 2;//屏幕每行使用的字节数
video_num_lines = ORIG_VIDEO_LINES;//字符列数,25
video_erase_char = 0x0720;//擦除字符属性及字符
video_mem_start = 0xb8000;//显示内存起始地址
video_port_reg = 0x3d4;//显示控制索引寄存器端口
video_port_val = 0x3d5;//显示控制数据寄存器端口
video_type = VIDEO_TYPE_EGAC;// EGA/VGA 彩色模式
video_mem_end = 0xbc000;//显示内存结束地址
origin = video_mem_start;//一屏的起始内存地址
scr_end = video_mem_start + video_num_lines * video_size_row;//一屏的末端内存地址
top = 0;//滚动时顶行行号
bottom = video_num_lines;//滚动时底行行号
//设置光标位置
gotoxy(ORIG_X,ORIG_Y);
//注册键盘中断函数
set_trap_gate(0x21,&keyboard_interrupt);
//下面操作是打开键盘中断
outb_p(inb_p(0x21)&0xfd,0x21);
a=inb_p(0x61);
outb_p(a|0x80,0x61);
outb(a,0x61);
}
//擦除光标前一个字符(用空格代替),并设置pos和x变量
static void del(void)
{
if (x) {//如果光标没有在0列
pos -= 2;//pos后退2个字节(一个字符对应两个字节)
x--;//光标位置x减1
*(unsigned short *)pos = video_erase_char;//擦除pos处字符
}
}
//光标调回第一列(0列)
static void cr(void)
{
pos -= x<<1;//因为一列占两个字节,所以减去x<<1
x=0;//x清零
}
//光标位置下移一行
static void lf(void)
{
if (y+1<bottom) {//如果光标没有在最后一行上
y++;//光标行坐标加1
pos += video_size_row;//video_size_row为每行使用的字节数,相当于加一行
return;
}
//scrup();
}
//根据光标对应显存位置pos,设置显示控制器光标的显示位置
static inline void set_cursor(void)
{
cli();//关中断
//向显示控制索引寄存器端口(0x3d4)写14
outb_p(14, video_port_reg);//选择数据寄存器r14
//向显示控制数据寄存器端口(0x3d5),写入光标当前位置的高字节
outb_p(0xff&((pos-video_mem_start)>>9), video_port_val);
//向显示控制索引寄存器端口(0x3d4)写14
outb_p(15, video_port_reg);//选择数据寄存器r15
//向显示控制数据寄存器端口(0x3d5),写入光标当前位置的低字节
outb_p(0xff&((pos-video_mem_start)>>1), video_port_val);
sti();//开中断
}
void con_write(struct tty_struct * tty)
{
int nr;
char c;
nr = CHARS(tty->write_q);
while (nr--) {
GETCH(tty->write_q,c);
if (c>31 && c<127) {
(*(char *)pos++) = c;
(*(char *)pos++) = attr;
x++;
}
else if (c==127) {
del();
}
else if (c==13){
cr();
lf();
}
}
set_cursor();
}
void copy_to_cooked(struct tty_struct * tty)
{
signed char c;
if(!EMPTY(tty->read_q) ){
GETCH(tty->read_q,c);
PUTCH(c,tty->write_q);
tty->write(tty);
}
}
void do_tty_interrupt(int tty)
{
copy_to_cooked(tty_table+tty);
}
system.h中添加
#define cli() __asm__ ("cli"::)