linux-0.11分析:init文件 main.c的第五个初始化函数tty_init()第七篇随笔

5、第五个初始化函数,tty_init()

参考 [github这个博主的 厉害][ https://github.com/sunym1993/flash-linux0.11-talk ]

先看看这个tty_init()的这段代码:

kernel文件 -> chr_drv文件 ->tty_io.c

void tty_init(void)
{
	rs_init();
	con_init();
}

可以看到这两个函数调用了rs_init()和con_init()这两个函数:

第一个方法:rs_init()

kernel文件 -> chr_drv文件 ->serial.c

void rs_init(void)
{
	set_intr_gate(0x24,rs1_interrupt);
	set_intr_gate(0x23,rs2_interrupt);
	init(tty_table[1].read_q.data);
	init(tty_table[2].read_q.data);
	outb(inb_p(0x21)&0xE7,0x21);
}

可以看出它里面又调用了三个函数:set_intr_gate、init、outb

可以看到第一个set_intr_gate很熟悉,就是在注册中断信息,注册了24(rs1_interrupt),23(rs2_interrupt)中断信息

set_intr_gate()

#define set_intr_gate(n,addr) \
_set_gate(&idt[n],14,0,addr)

init(),传入的就是tty_table这个结构体传入的端口

static void init(int port)
{
	outb_p(0x80,port+3);	/* 设置线路控制寄存器的DLAB */
	outb_p(0x30,port);		/* 除数的倒数(48->2400 bps) */
	outb_p(0x00,port+1);	/* 除数的/*MS */
	outb_p(0x03,port+3);	/* 重置DLAB */
	outb_p(0x0b,port+4);	/* 设置DTR、RTS和OUT_ 2 */
	outb_p(0x0d,port+1);	/* 启用除写入之外的所有Intr */
	(void)inb(port);		/* 读取数据端口以重置内容(?) */
}

这里就是通过outb_p()函数设置了一些东西,具体我也不是很了解

第二个方法:con_init()这个方法比较长,我们一部分一部分来看

kernel文件 -> chr_drv文件 ->console.c

void con_init(void)
{
	register unsigned char a;
	char *display_desc = "????";
	char *display_ptr;

	video_num_columns = ORIG_VIDEO_COLS; //(((*(unsigned short *)0x90006) & 0xff00) >> 8)
	video_size_row = video_num_columns * 2;
	video_num_lines = ORIG_VIDEO_LINES;
	video_page = ORIG_VIDEO_PAGE;
	video_erase_char = 0x0720;
	
	if (ORIG_VIDEO_MODE == 7)			/* Is this a monochrome display? */
	{
		video_mem_start = 0xb0000;
		video_port_reg = 0x3b4;
		video_port_val = 0x3b5;
		if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
		{
			video_type = VIDEO_TYPE_EGAM;
			video_mem_end = 0xb8000;
			display_desc = "EGAm";
		}
		else
		{
			video_type = VIDEO_TYPE_MDA;
			video_mem_end	= 0xb2000;
			display_desc = "*MDA";
		}
	}
	else								/* If not, it is color. */
	{
		video_mem_start = 0xb8000;
		video_port_reg	= 0x3d4;
		video_port_val	= 0x3d5;
		if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
		{
			video_type = VIDEO_TYPE_EGAC;
			video_mem_end = 0xbc000;
			display_desc = "EGAc";
		}
		else
		{
			video_type = VIDEO_TYPE_CGA;
			video_mem_end = 0xba000;
			display_desc = "*CGA";
		}
	}

	/* Let the user known what kind of display driver we are using */
	
	display_ptr = ((char *)video_mem_start) + video_size_row - 8;
	while (*display_desc)
	{
		*display_ptr++ = *display_desc++;
		display_ptr++;
	}
	
	/* Initialize the variables used for scrolling (mostly EGA/VGA)	*/
	
	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);
}

第一部分:

首先先看看这个文件上面的一些define:提取对下面有用的

#define ORIG_X			(*(unsigned char *)0x90000)
#define ORIG_Y			(*(unsigned char *)0x90001)
#define ORIG_VIDEO_PAGE		(*(unsigned short *)0x90004)
#define ORIG_VIDEO_MODE		((*(unsigned short *)0x90006) & 0xff)
#define ORIG_VIDEO_COLS 	(((*(unsigned short *)0x90006) & 0xff00) >> 8)
#define ORIG_VIDEO_LINES	(25)
#define ORIG_VIDEO_EGA_AX	(*(unsigned short *)0x90008)
#define ORIG_VIDEO_EGA_BX	(*(unsigned short *)0x9000a)
#define ORIG_VIDEO_EGA_CX	(*(unsigned short *)0x9000c)

#define VIDEO_TYPE_MDA		0x10	/* Monochrome Text Display	*/
#define VIDEO_TYPE_CGA		0x11	/* CGA Display 			*/
#define VIDEO_TYPE_EGAM		0x20	/* EGA/VGA in Monochrome Mode	*/
#define VIDEO_TYPE_EGAC		0x21	/* EGA/VGA in Color Mode	*/

#define NPAR 16
......
register unsigned char a;
char *display_desc = "????";
char *display_ptr;

video_num_columns  = ORIG_VIDEO_COLS; //这个是在设置为,显示模式
// video_num_columns = (((*(unsigned short *)0x90006) & 0xff00) >> 8)
video_size_row = video_num_columns * 2;
video_num_lines = ORIG_VIDEO_LINES; //是在设置行数
// video_num_lines = (25)
video_page = ORIG_VIDEO_PAGE;	//在设置显示页面
// video_page = (*(unsigned short *)0x90004)
video_erase_char = 0x0720;
......

这是就是在初始化一些东西,video_xxx_xx都是一些全局变量,后面的都是一些定义好的数据在这个文件的上面

还记得在setup.s阶段有一个存储设备相关信息表

地址 字节 存储的东西
0x90000 1 光标列号
0x90001 1 光标行号
0x90002 2 内存大小
0x90004 2 显示页面
0x90006 1 显示模式
0x90007 1 窗口宽度
0x90008 2 配置参数
0x9000A 2 配置参数
0x9000C 2 配置参数
0x90080 16 hd0硬盘信息
0x90010 16 hd1硬盘信息

第二部分:

	if (ORIG_VIDEO_MODE == 7)			/* 这是单色显示器吗 */
	{
		video_mem_start = 0xb0000;
		video_port_reg = 0x3b4;
		video_port_val = 0x3b5;
		if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
		{
			video_type = VIDEO_TYPE_EGAM; 	0x20
			video_mem_end = 0xb8000;
			display_desc = "EGAm";
		}
		else
		{
			video_type = VIDEO_TYPE_MDA;	0x10
			video_mem_end	= 0xb2000;
			display_desc = "*MDA";
		}
	}
	else								/* 它配置了颜色. */
	{
		video_mem_start = 0xb8000;
		video_port_reg	= 0x3d4;
		video_port_val	= 0x3d5;
		if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
		{
			video_type = VIDEO_TYPE_EGAC;	0x21
			video_mem_end = 0xbc000;
			display_desc = "EGAc";
		}
		else
		{
			video_type = VIDEO_TYPE_CGA;	0x11
			video_mem_end = 0xba000;
			display_desc = "*CGA";
		}
	}

基本上可以看出就是一些不同if else判断设置一些东西;设置:video_mem_start、video_port_reg、video_port_val、video_type、video_mem_end、display_desc这些参数

总共有四个分支

	第一个分支结果												第二个分支结果
video_mem_start = 0xb0000;							video_mem_start = 0xb0000;
video_port_reg = 0x3b4; 							video_port_reg = 0x3b4;
video_port_val = 0x3d5; 							video_port_val = 0x3d5;
video_type = 0x20; 									video_type = 0x10; 
video_mem_end = 0xb8000;							video_mem_end = 0xb2000;
display_desc = "EGAm";								display_desc = "*MDA";	

	第三个分支结果												第四个分支结果
video_mem_start = 0xb8000;							video_mem_start = 0xb8000;
video_port_reg = 0x3d4; 							video_port_reg = 0x3d4;
video_port_val = 0x3b5; 							video_port_val = 0x3b5;
video_type = 0x21; 									video_type = 0x11; 
video_mem_end = 0xbc000;							video_mem_end = 0xba000;
display_desc = "EGAc";								display_desc = "*CGA";	

第三部分:

/*让用户知道我们使用的是哪种显示驱动程序 */

display_ptr = ((char *)video_mem_start) + video_size_row - 8;
while (*display_desc)
{
    *display_ptr++ = *display_desc++;
    display_ptr++;
}
	

这是通过上面的结果去判断是那种显示的驱动

第四部分:

/* 初始化用于滚动的变量(主要是EGA/VGA)	*/

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);
//  ORIG_X 			(*(unsigned char *)0x90000)光标列号
//  ORIG_Y			(*(unsigned char *)0x90001)光标行号
    
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);

这是在设置光标位置,开启0x21键盘中断

这里学过汇编就应该知道,一般开起int 0x21中断后,就会根据不同寄存器的值跳转到中断位置进行操作

gotoxy(ORIG_X,ORIG_Y); 就在进行操了,看看这个函数

static unsigned long	pos;
static unsigned long	x,y;

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;
	pos=origin + y*video_size_row + (x<<1);
}

这里先判断是否超过行或者列

在用x,y,pos来保存,x:列号,y:行号,pos:对应的内存地址

然后就是开起0x21中断:

具体过程如下:

这个中断在这个文件下的keyboard.S中,比较长就不一一列举了

_keyboard_interrupt:
......
call _do_tty_interrupt
......

在kernel文件 -> chr_drv文件 ->tty_io.c

void do_tty_interrupt(int tty)
{
	copy_to_cooked(tty_table+tty);
}
void copy_to_cooked(struct tty_struct * tty)
{
	signed char c;
    ......
    tty->write(tty);
    .....
    wake_up(&tty->secondary.proc_list);
}

在kernel文件 -> chr_drv文件 ->console.c

void con_write(struct tty_struct * tty)
{
	int nr;
	char c;
    ......
    __asm__("movb _attr,%%ah\n\t"
                "movw %%ax,%1\n\t"
                ::"a" (c),"m" (*(short *)pos)
                :"ax");
    pos += 2;
    x++;
    ......

可以看到这段内联汇编,在进行光标的移动操作;省略的一部分就是一边界操作:都在console.c的函数

static void lf(void)	//换行
static void ri(void)	//退行
   ......

就不一一列举了

到这里就结束了这个tty_init()初始化;这个时候键盘的功能解决大致完成了,就看通过键盘输入来进行中断了

posted @ 2022-08-08 13:45  水三丫  阅读(164)  评论(0编辑  收藏  举报