在上篇中,在填充完寄存器之后也不等待硬盘中断处理程序结束就直接返回了,在这里我们就需要等待,在Disp_HD_Info调用Wait_Int函数:
kernel/hd.c
- /*----------------------------------------------------------------------Wait_Int
- 等待硬盘中断程序执行完毕
- */
- static void Wait_Int()
- {
- Message m;
- m.msg_type = HARD_INT;
- Send_Receive_Shell(RECEIVE,INTERRUPT,&m);
- }
注意这里修改了MSG_Receive函数,新增加的代码如下:
kernel/ipc.c
- /* 如果在等待一个外中断的消息 */
- if((m->msg_type == HARD_INT) && (send_pid == INTERRUPT))
- {
- /* 如果该中断在此之前已经执行完毕,则把标志重置为0返回 */
- if(caller->has_int_msg == 1)
- {
- caller->has_int_msg = 0;
- return;
- }
- /* 否则则阻塞当前进程,并重新调度 */
- else
- {
- caller->ipc_status |= RECEIVING;
- caller->ipc_status &= ~NO_BLOCK;
- Schedule();
- return;
- }
- }
同时修改硬盘中断处理函数:
kernel/hd.c
- /*-------------------------------------------------------------Hard_Disk_Handler
- 硬盘中断处理程序
- */
- static void Hard_Disk_Handler(int int_vec_no)
- {
- In_Byte(REG_STATUS); /* 必须从状态寄存器清空状态值 */
- /* 处理在等待中断结束的进程 */
- PCB *sender = PCB_Table + PROC_HD_PID; /* 明确指定硬盘驱动进程 */
- sender->has_int_msg = 1; /* 代表已经处理过 */
- /* 使硬盘驱动进程解除阻塞 */
- sender->ipc_status &= ~RECEIVING;
- sender->ipc_status |= NO_BLOCK;
- }
用到的相关宏如下:
include/ipc.h
- #define INTERRUPT -3 /* 由中断处理程序发送或者接收的消息 */
include/const.h
- #define HARD_INT 2 /* 表示等待外中断功能号 */
在PCB中添加has_int_msg字段:
include/proc.c
- int has_int_msg; /* 表示是否中断处理程序是否结束 */
在初始化PCB时也别忘了这个字段,在Init_PCB函数中添加:
kernel/proc.c
- p_Cur_PCB->has_int_msg = 0;
OK,我们现在可以把硬盘信息从端口读到一个缓冲区中,查阅资料得到这个信息是256个word,因此就是512字节,我们先定义一个缓冲区,再在Disp_HD_Info函数中追加调用一个函数把数据写到这个缓冲区中:
kernel/hd.c
- /* 内部数据声明 */
- static u8 hd_buf[512]; /* 硬盘信息缓冲区 */
- ...
- //Disp_HD_Info函数末追加
- Read_Port_2_Buffer(REG_DATA,hd_buf,256); /* 把硬盘信息读出来放到缓冲区中 */
这个函数用汇编写的,因为在读端口时有一点与以前有些不同:
lib/lib_kernel_in_asm.asm:
- global Read_Port_2_Buffer
- ...
- ;-------------------------------------------------------------Read_Port_2_Buffer
- Read_Port_2_Buffer:
- ;insw为串输入指令,以字单位,该指令的功能是从dx指定的端口读入一个字节
- ;到es:di指定的内存单元中
- ;由于数据以1个字为一组的,所以必须一次读1个字
- mov edx,[esp + 4] ;端口号送edx
- mov edi,[esp + 8] ;缓冲区偏移送edi
- mov ecx,[esp + 12] ;要复制的字的个数送ecx
- cld ;clear df
- rep insw
- ret
在include/proto.h添加声明,过程略。
OK,数据已经存在于我们的缓冲区了,由于数据太多,我们要有选择的输出,首先在Disp_HD_Info函数追加:
kernel/hd.c
- Show_HD_Info(hd_buf); /* 选择一些信息实现出来 */
Show_HD_Info函数体如下,注释得还可以了:
kernel/hd.c
- /*------------------------------------------------------------------Show_HD_Info
- 把缓冲区中的数据有选择的输出
- */
- static void Show_HD_Info(void *hd_buf)
- {
- u16 *buf = (u16*)hd_buf;
- char tmp[60]; /* 临时串的BUFFER */
- int i,j;
- /*
- 我们想显示硬盘的序列码和模式,这里以一个结构来组织相关信息
- 看起来有点拐弯抹角,但如果要显示多个占有多个字的信息项
- 这样可以有效减少重复代码和方便扩展
- 序列码起始字号为10,占用10个字,说明串为...
- 模式名起始字号为27,占用10个字,说明串为...
- */
- HD_Str_Info str_info[2] = {
- {10,10,"Hard Disk Serial Num:"},
- {27,20,"Hard Disk Model:"}
- };
- for(i = 0;i < sizeof(str_info) / sizeof(str_info[0]);i++)
- {
- /* 指向每个数据项的首个数据块 */
- char *p = (char*)(buf + str_info[i].offset);
- /* 把串转移到临时缓冲区中 */
- for(j = 0;j < str_info[i].num;j++)
- {
- /*
- 这里注意,数据是以字来组织,而一个字符为一个字节
- 这里是小端存储,也就是遵低低高高原则
- 所以在一个字中高8位代表的字符实际顺序上在低8位字符之后
- */
- tmp[j * 2 + 1] = *p++;
- tmp[j * 2] = *p++;
- }
- tmp[j * 2] = '/0'; /* 置串结束符 */
- Printf("%s%s/n",str_info[i].label,tmp); /* 连同说明串打印出来 */
- }
- /* 如果buf[49]的第9位为1,表示支持LBA模式 */
- Printf("LBA supported:%s/n",(buf[49] & 0x200) ? "YES" : "NO");
- /* 如果buf[83]的第10位为1,表示支持LBA的48位寻址模式 */
- Printf("LBA48 supported:%s/n",(buf[83] & 0x400) ? "YES" : "NO");
- /* buf[60]为扇区总数的低16位,buf[61]为扇区总数的高16位 */
- int sector_num = ((int)buf[61] << 16) + buf[60];
- /* 单位转换为MB打印出来 */
- Printf("Hard Disk Size:%dMB/n",sector_num * 512 / 1000000);
- }
其中用到的结构如下:
include/hd.h
- /* 硬盘信息占用多个数据块的信息项 */
- typedef struct s_hd_str_info
- {
- int offset; /* 某数据项的首个数据块的偏移 */
- int num; /* 某数据项占用的数据块的总数 */
- char *label; /* 某数据块的说明 */
- }HD_Str_Info;
OK,make,bochs,结果如下: