读写硬盘

    下面就来添加读写硬盘的功能,首先修改Proc_Hard_Disk:

    kernel/hd.c

Code:
  1. case HD_READ:          
  2. case HD_WRITE:   
  3.     Read_And_Write_HD(&m);   
  4.     break;  

    下面来添加Read_And_Write函数,还把等待某端口的某位是否为0的功能抽象为Wait_For_Reg函数,相应的HD_Cmd_Out函数中也要调用,这里,很简单不细说:

    kernel/hd.c

Code:
  1. /*-------------------------------------------------------------Read_And_Write_HD   
  2.     读写硬盘函数  
  3. */  
  4. static void Read_And_Write_HD(Message *m)   
  5. {   
  6.     int device = m->i1;     /* 取出设备号 */  
  7.     /*   
  8.         取出相应分区的起始扇区号   
  9.         如果是设备号代表的是整个硬盘和主分区,则到primary中寻找  
  10.         如果是扩展分区的逻辑分区则到logical中寻找  
  11.     */  
  12.     int prt_begin_sec = (device < 5) ?    
  13.                             hd_prt_info.primary[device].base   
  14.                             : hd_prt_info.logical[device - 16].base;   
  15.                                
  16.     int read_begin_sec = prt_begin_sec + m->i2; /* 取出要读写的第一个扇区号 */  
  17.     int read_bytes = m->i3;         /* 取出要读写的字节数 */  
  18.     void *ret_buf = (void*)m->r1;   /* 取出含有要读写的数据的指针 */  
  19.        
  20.     /* 设置正确的读取硬盘的扇区数 */  
  21.     int sc;   
  22.     if(read_bytes < SECTOR_SIZE)   
  23.     {   
  24.         sc = 1;   
  25.     }      
  26.     else if((read_bytes % SECTOR_SIZE) == 0)   
  27.     {   
  28.         sc = read_bytes / SECTOR_SIZE;   
  29.     }   
  30.     else  
  31.     {   
  32.         sc = read_bytes / SECTOR_SIZE + 1;   
  33.     }   
  34.        
  35.     /* 填充准备发出的命令 */  
  36.     HD_Command hd_cmd;   
  37.     hd_cmd.features = 0;   
  38.     hd_cmd.sector_count = sc;   
  39.     hd_cmd.lba_low = read_begin_sec & 0xff;   
  40.     hd_cmd.lba_mid = (read_begin_sec >> 8) & 0xff;   
  41.     hd_cmd.lba_high = (read_begin_sec >> 16) & 0xff;   
  42.     hd_cmd.device = MAKE_DEVICE_REG(1,0,(read_begin_sec >> 24) & 0xf);   
  43.     hd_cmd.command = (m->msg_type == HD_READ) ? ATA_READ_HD : ATA_WRITE_HD; /* 读还是写? */  
  44.        
  45.     HD_Cmd_Out(&hd_cmd);   
  46.        
  47.     /* 如果是读,则等待硬盘驱动器把数据读到REG_DATA中 */  
  48.     if(m->msg_type == HD_READ)   
  49.     {   
  50.         Wait_Int();   
  51.     }   
  52.        
  53.     int left_bytes = read_bytes;   
  54.     int copy_bytes = SECTOR_SIZE;   
  55.        
  56.     /* 一次读写一个扇区,如果还有剩余则继续 */  
  57.     while(left_bytes > 0)   
  58.     {   
  59.         /* 读来到这 */  
  60.         if(m->msg_type == HD_READ)   
  61.         {   
  62.             /* 如果成立,则说明读完这次就结束了 */  
  63.             if(left_bytes < SECTOR_SIZE)   
  64.             {   
  65.                 copy_bytes = left_bytes;   
  66.                 /* 由于从REG_DATA一次读2个字节,则如果copy_bytes为奇数,则要自增1 */  
  67.                 if((copy_bytes % 2) != 0)   
  68.                 {   
  69.                     copy_bytes++;   
  70.                 }   
  71.             }   
  72.             /* 从REG_DATA把数据读到hd_buf缓冲区中,copy_bytes / 2是因为一次读2字节 */  
  73.             Read_Port_2_Buffer(REG_DATA,hd_buf,copy_bytes / 2);    
  74.             Memory_Copy(ret_buf,hd_buf,copy_bytes);     /* 把数据再从缓冲区读到返回的缓冲区 */  
  75.         }   
  76.         /* 写来到这 */  
  77.         else  
  78.         {   
  79.             /*   
  80.                 写硬盘的话如果是写一个扇区的话,必须填充REG_DATA256次。   
  81.                 所以这里如果写的数据少于一个扇区的话,则把数据量设为一个扇区。  
  82.             */  
  83.             if(left_bytes < SECTOR_SIZE)   
  84.             {   
  85.                 copy_bytes = SECTOR_SIZE;   
  86.             }   
  87.             /* 如果REG_STATUS最高位不为0,说明硬盘尚未准备好,不能写数据 */  
  88.             Wait_For_Reg(REG_STATUS,STATUS_GO_ON,100);     
  89.             /* 写一个扇区的数据到REG_DATA中 */  
  90.             Write_Buffer_2_Port(REG_DATA,ret_buf,copy_bytes / 2);   
  91.             Wait_Int(); /* 写完一个扇区后,要等待硬盘工作完成,才可写下一个扇区 */  
  92.         }   
  93.            
  94.         ret_buf += SECTOR_SIZE;     /* 消息的返回数据缓冲区指针指向下一个512字节 */  
  95.         left_bytes -= SECTOR_SIZE;  /* 要写的数据字节数减512 */  
  96.     }   
  97. }   
  98.   
  99. /*------------------------------------------------------------------Wait_For_Reg    
  100.     等待某个端口的某位为0,如果不为0则死等,milli_sec是查询的间隔。  
  101.     中断处理函数中不可调用,因为调用了Milli_Delay ?  
  102. */  
  103. static void Wait_For_Reg(u16 port,u8 mask,int milli_sec)   
  104. {   
  105.     do  
  106.     {   
  107.         u8 cur_status = In_Byte(port);   
  108.         if((cur_status & mask) == 0)   
  109.         {   
  110.             break;   
  111.         }   
  112.         Milli_Delay(milli_sec);   
  113.     }while(1);   
  114. }  

    其中用到了Write_Buffer_2_Port函数,功能跟Read_Port_2_Buffer函数功能相反。。不同之处一个用si表示缓冲区地址另一个用di。。

    lib/lib_in_asm.asm:

Code:
  1. ;------------------------------------------------------------Write_Buffer_2_Port   
  2. Write_Buffer_2_Port:   
  3. ;outsw为串输出指令,以字为单位,该指令的功能是从dx指定的端口写入一个字节   
  4. ;从es:si指定的内存单元读取   
  5.     mov edx,[esp + 4]       ;端口号送edx   
  6.     mov esi,[esp + 8]       ;缓冲区偏移送edi   
  7.     mov ecx,[esp + 12]      ;要复制的字的个数送ecx   
  8.        
  9.     cld                     ;clear df   
  10.     rep outsw   
  11.     ret   

    在proto.h中添加原型则不消说了。

    使用到的相关宏定义如下:

    include/const.h

Code:
  1. #define HD_READ                     3           /* 读硬盘的功能号 */   
  2. #define HD_WRITE                    4           /* 写硬盘的功能号 */  

    include/hd.h

Code:
  1. #define ATA_READ_HD         0x20    /* 读取硬盘的送给硬盘驱动器寄存器的值 */   
  2. #define ATA_WRITE_HD        0x30    /* 写硬盘的送给硬盘驱动器寄存器的值 */   
  3.   
  4. #define SECTOR_SIZE         512     /* 一个扇区的大小 */   
  5. #define STATUS_GO_ON        0x80    /* 状态寄存器的测试BUSY位是否为0的MASK */  

    下面我们就来做测试了,先读系统分区表的内容,就是0扇区的0x1be偏移处的64个字节,修改FS进程的执行体:

    kernel/fs.c

Code:
  1. /*--------------------------------------------------------------Proc_File_System  
  2.     文件系统进程的执行体  
  3. */  
  4.   
  5. static  u8 buf[1024];   /* 读写硬盘的缓冲区 */  
  6.   
  7. void Proc_File_System()   
  8. {   
  9.     Message m;   
  10.     m.msg_type = HD_INFO;   
  11.     Send_Receive_Shell(BOTH,PROC_HD_PID,&m);   
  12.        
  13.     m.msg_type = HD_READ;   
  14.     m.i1 = 0;   
  15.     m.i2 = 0;   
  16.     m.i3 = 512;   
  17.     m.r1 = (void*)buf;   
  18.     Send_Receive_Shell(BOTH,PROC_HD_PID,&m);   
  19.        
  20.     int i = 0;   
  21.     while(i < 16 * 4)   
  22.     {   
  23.         Printf("%c ",buf[0x1be + i++]);   
  24.     }   
  25.        
  26.     Printf("PROC FS LOOP/n");   
  27.     while(1);   
  28. }  

    OK,make,bochs,结果如图:

    跟实际情况是否一致呢,看看硬盘里的对应部分的数据:

    我们再来看看写功能,先在设备号为33的逻辑扇区的起始扇区写一个512的数据,再把这个地儿的数据的前3个字节读出来并显示出来,代码如下:

    kernel/fs.c

Code:
  1. /*--------------------------------------------------------------Proc_File_System  
  2.     文件系统进程的执行体  
  3. */  
  4.   
  5. static  u8 buf[1024];   /* 读写硬盘的缓冲区 */  
  6.   
  7. void Proc_File_System()   
  8. {   
  9.     buf[0] = 'a';   
  10.     buf[1] = 'b';   
  11.     buf[2] = 'c';   
  12.     int j = 4;   
  13.     for(;j < 1024;j++){   
  14.         buf[j] = 0;   
  15.     }   
  16.        
  17.     Message m;   
  18.     m.msg_type = HD_INFO;   
  19.     Send_Receive_Shell(BOTH,PROC_HD_PID,&m);   
  20.        
  21.     m.msg_type = HD_WRITE;   
  22.     m.i1 = 33;   
  23.     m.i2 = 0;   
  24.     m.i3 = 3;   
  25.     m.r1 = (void*)buf;   
  26.     Send_Receive_Shell(BOTH,PROC_HD_PID,&m);   
  27.        
  28.     j = 0;   
  29.     for(;j < 1024;j++)   
  30.     {   
  31.         buf[j] = 0;   
  32.     }   
  33.        
  34.     m.msg_type = HD_READ;   
  35.     m.i1 = 33;   
  36.     m.i2 = 0;   
  37.     m.i3 = 512;   
  38.     m.r1 = (void*)buf;   
  39.     Send_Receive_Shell(BOTH,PROC_HD_PID,&m);   
  40.        
  41.     int i = 0;   
  42.     while(i < 3)   
  43.     {   
  44.         Printf("%c/n",buf[i++]);   
  45.     }   
  46.            
  47.     Printf("PROC FS LOOP/n");   
  48.     while(1);   
  49. }  

    OK,make,bochs,结果如图所示:

    再来看看硬盘里的情况,偏移字节数为:0xec7f * 512 = 0x 1d8fe00,如图所示:

    嗯,很好,成功了。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值