📄 floppy.c
字号:
}//// 软盘操作出错中断调用函数。由软驱中断处理程序调用。static voidbad_flp_intr (void){ CURRENT->errors++; // 当前请求项出错次数增1。// 如果当前请求项出错次数大于最大允许出错次数,则取消选定当前软驱,并结束该请求项(不更新)。 if (CURRENT->errors > MAX_ERRORS) { floppy_deselect (current_drive); end_request (0); }// 如果当前请求项出错次数大于最大允许出错次数的一半,则置复位标志,需对软驱进行复位操作,// 然后再试。否则软驱需重新校正一下,再试。 if (CURRENT->errors > MAX_ERRORS / 2) reset = 1; else recalibrate = 1;} /* * Ok, this interrupt is called after a DMA read/write has succeeded, * so we check the results, and copy any buffers. *//** OK,下面该中断处理函数是在DMA 读/写成功后调用的,这样我们就可以检查执行结果,* 并复制缓冲区中的数据。*///// 软盘读写操作成功中断调用函数。。static voidrw_interrupt (void){// 如果返回结果字节数不等于7,或者状态字节0、1 或2 中存在出错标志,则若是写保护// 就显示出错信息,释放当前驱动器,并结束当前请求项。否则就执行出错计数处理。// 然后继续执行软盘请求操作。// ( 0xf8 = ST0_INTR | ST0_SE | ST0_ECE | ST0_NR )// ( 0xbf = ST1_EOC | ST1_CRC | ST1_OR | ST1_ND | ST1_WP | ST1_MAM,应该是0xb7)// ( 0x73 = ST2_CM | ST2_CRC | ST2_WC | ST2_BC | ST2_MAM ) if (result () != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) { if (ST1 & 0x02) { // 0x02 = ST1_WP - Write Protected。 printk ("Drive %d is write protected\n\r", current_drive); floppy_deselect (current_drive); end_request (0); } else bad_flp_intr (); do_fd_request (); return; }// 如果当前请求项的缓冲区位于1M 地址以上,则说明此次软盘读操作的内容还放在临时缓冲区内,// 需要复制到请求项的缓冲区中(因为DMA 只能在1M 地址范围寻址)。 if (command == FD_READ && (unsigned long) (CURRENT->buffer) >= 0x100000) copy_buffer (tmp_floppy_area, CURRENT->buffer);// 释放当前软盘,结束当前请求项(置更新标志),再继续执行其它软盘请求项。 floppy_deselect (current_drive); end_request (1); do_fd_request ();}//// 设置DMA 并输出软盘操作命令和参数(输出1 字节命令+ 0~7 字节参数)。inline voidsetup_rw_floppy (void){ setup_DMA (); // 初始化软盘DMA 通道。 do_floppy = rw_interrupt; // 置软盘中断调用函数指针。 output_byte (command); // 发送命令字节。 output_byte (head << 2 | current_drive); // 发送参数(磁头号+驱动器号)。 output_byte (track); // 发送参数(磁道号)。 output_byte (head); // 发送参数(磁头号)。 output_byte (sector); // 发送参数(起始扇区号)。 output_byte (2); /* sector size = 512 */// 发送参数(字节数(N=2)512 字节)。 output_byte (floppy->sect); // 发送参数(每磁道扇区数)。 output_byte (floppy->gap); // 发送参数(扇区间隔长度)。 output_byte (0xFF); /* sector size (0xff when n!=0 ?) */// 发送参数(当N=0 时,扇区定义的字节长度),这里无用。// 若在发送命令和参数时发生错误,则继续执行下一软盘操作请求。 if (reset) do_fd_request ();} /* * This is the routine called after every seek (or recalibrate) interrupt * from the floppy controller. Note that the "unexpected interrupt" routine * also does a recalibrate, but doesn't come here. *//** 该子程序是在每次软盘控制器寻道(或重新校正)中断后被调用的。注意* "unexpected interrupt"(意外中断)子程序也会执行重新校正操作,但不在此地。*///// 寻道处理中断调用函数。// 首先发送检测中断状态命令,获得状态信息ST0 和磁头所在磁道信息。若出错则执行错误计数// 检测处理或取消本次软盘操作请求项。否则根据状态信息设置当前磁道变量,然后调用函数// setup_rw_floppy()设置DMA 并输出软盘读写命令和参数。static voidseek_interrupt (void){/* sense drive status *//* 检测中断状态 */// 发送检测中断状态命令,该命令不带参数。返回结果信息两个字节:ST0 和磁头当前磁道号。 output_byte (FD_SENSEI);// 如果返回结果字节数不等于2,或者ST0 不为寻道结束,或者磁头所在磁道(ST1)不等于设定磁道,// 则说明发生了错误,于是执行检测错误计数处理,然后继续执行软盘请求项,并退出。 if (result () != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { bad_flp_intr (); do_fd_request (); return; } current_track = ST1; // 设置当前磁道。 setup_rw_floppy (); // 设置DMA 并输出软盘操作命令和参数。} /* * This routine is called when everything should be correctly set up * for the transfer (ie floppy motor is on and the correct floppy is * selected). *//** 该函数是在传输操作的所有信息都正确设置好后被调用的(也即软驱马达已开启* 并且已选择了正确的软盘(软驱)。*///// 读写数据传输函数。static voidtransfer (void){// 首先看当前驱动器参数是否就是指定驱动器的参数,若不是就发送设置驱动器参数命令及相应// 参数(参数1:高4 位步进速率,低四位磁头卸载时间;参数2:磁头加载时间)。 if (cur_spec1 != floppy->spec1) { cur_spec1 = floppy->spec1; output_byte (FD_SPECIFY); // 发送设置磁盘参数命令。 output_byte (cur_spec1); /* hut etc */// 发送参数。 output_byte (6); /* Head load time =6ms, DMA */ }// 判断当前数据传输速率是否与指定驱动器的一致,若不是就发送指定软驱的速率值到数据传输// 速率控制寄存器(FD_DCR)。 if (cur_rate != floppy->rate) outb_p (cur_rate = floppy->rate, FD_DCR);// 若返回结果信息表明出错,则再调用软盘请求函数,并返回。 if (reset) { do_fd_request (); return; }// 若寻道标志为零(不需要寻道),则设置DMA 并发送相应读写操作命令和参数,然后返回。 if (!seek) { setup_rw_floppy (); return; }// 否则执行寻道处理。置软盘中断处理调用函数为寻道中断函数。 do_floppy = seek_interrupt;// 如果器始磁道号不等于零则发送磁头寻道命令和参数 if (seek_track) { output_byte (FD_SEEK); // 发送磁头寻道命令。 output_byte (head << 2 | current_drive); //发送参数:磁头号+当前软驱号。 output_byte (seek_track); // 发送参数:磁道号。 } else { output_byte (FD_RECALIBRATE); // 发送重新校正命令。 output_byte (head << 2 | current_drive); //发送参数:磁头号+当前软驱号。 }// 如果复位标志已置位,则继续执行软盘请求项。 if (reset) do_fd_request ();} /* * Special case - used after a unexpected interrupt (or reset) *//** 特殊情况 - 用于意外中断(或复位)处理后。*///// 软驱重新校正中断调用函数。// 首先发送检测中断状态命令(无参数),如果返回结果表明出错,则置复位标志,否则复位重新// 校正标志。然后再次执行软盘请求。static voidrecal_interrupt (void){ output_byte (FD_SENSEI); // 发送检测中断状态命令。 if (result () != 2 || (ST0 & 0xE0) == 0x60) // 如果返回结果字节数不等于2 或命令 reset = 1; // 异常结束,则置复位标志。 else // 否则复位重新校正标志。 recalibrate = 0; do_fd_request (); // 执行软盘请求项。}//// 意外软盘中断请求中断调用函数。// 首先发送检测中断状态命令(无参数),如果返回结果表明出错,则置复位标志,否则置重新// 校正标志。voidunexpected_floppy_interrupt (void){ output_byte (FD_SENSEI); // 发送检测中断状态命令。 if (result () != 2 || (ST0 & 0xE0) == 0x60) // 如果返回结果字节数不等于2 或命令 reset = 1; // 异常结束,则置复位标志。 else // 否则置重新校正标志。 recalibrate = 1;}//// 软盘重新校正处理函数。// 向软盘控制器FDC 发送重新校正命令和参数,并复位重新校正标志。static voidrecalibrate_floppy (void){ recalibrate = 0; // 复位重新校正标志。 current_track = 0; // 当前磁道号归零。 do_floppy = recal_interrupt; // 置软盘中断调用函数指针指向重新校正调用函数。 output_byte (FD_RECALIBRATE); // 发送命令:重新校正。 output_byte (head << 2 | current_drive); // 发送参数:(磁头号加)当前驱动器号。 if (reset) // 如果出错(复位标志被置位)则继续执行软盘请求。 do_fd_request ();}//// 软盘控制器FDC 复位中断调用函数。在软盘中断处理程序中调用。// 首先发送检测中断状态命令(无参数),然后读出返回的结果字节。接着发送设定软驱参数命令// 和相关参数,最后再次调用执行软盘请求。static voidreset_interrupt (void){ output_byte (FD_SENSEI); // 发送检测中断状态命令。 (void) result (); // 读取命令执行结果字节。 output_byte (FD_SPECIFY); // 发送设定软驱参数命令。 output_byte (cur_spec1); /* hut etc */// 发送参数。 output_byte (6); /* Head load time =6ms, DMA */ do_fd_request (); // 调用执行软盘请求。} /* * reset is done by pulling bit 2 of DOR low for a while. *//* FDC 复位是通过将数字输出寄存器(DOR)位2 置0 一会儿实现的 *///// 复位软盘控制器。static voidreset_floppy (void){ int i; reset = 0; // 复位标志置0。 cur_spec1 = -1; cur_rate = -1; recalibrate = 1; // 重新校正标志置位。 printk ("Reset-floppy called\n\r"); // 显示执行软盘复位操作信息。 cli (); // 关中断。 do_floppy = reset_interrupt; // 设置在软盘中断处理程序中调用的函数。 outb_p (current_DOR & ~0x04, FD_DOR); // 对软盘控制器FDC 执行复位操作。 for (i = 0; i < 100; i++) // 空操作,延迟。 __asm__ ("nop"); outb (current_DOR, FD_DOR); // 再启动软盘控制器。 sti (); // 开中断。}//// 软驱启动定时中断调用函数。// 首先检查数字输出寄存器(DOR),使其选择当前指定的驱动器。然后调用执行软盘读写传输// 函数transfer()。static voidfloppy_on_interrupt (void){ /* We cannot do a floppy-select, as that might sleep. We just force it *//* 我们不能任意设置选择的软驱,因为这样做可能会引起进程睡眠。我们只是迫使它自己选择 */ selected = 1; // 置已选择当前驱动器标志。// 如果当前驱动器号与数字输出寄存器DOR 中的不同,则重新设置DOR 为当前驱动器current_drive。// 定时延迟2 个滴答时间,然后调用软盘读写传输函数transfer()。否则直接调用软盘读写传输函数。 if (current_drive != (current_DOR & 3)) { current_DOR &= 0xFC; current_DOR |= current_drive; outb (current_DOR, FD_DOR); // 向数字输出寄存器输出当前DOR。 add_timer (2, &transfer); // 添加定时器并执行传输函数。 } else transfer (); // 执行软盘读写传输函数。}//// 软盘读写请求项处理函数。//voiddo_fd_request (void){ unsigned int block; seek = 0;// 如果复位标志已置位,则执行软盘复位操作,并返回。 if (reset) { reset_floppy (); return; }// 如果重新校正标志已置位,则执行软盘重新校正操作,并返回。 if (recalibrate) { recalibrate_floppy (); return; }// 检测请求项的合法性(参见kernel/blk_drv/blk.h,127)。 INIT_REQUEST;// 将请求项结构中软盘设备号中的软盘类型(MINOR(CURRENT->dev)>>2)作为索引取得软盘参数块。 floppy = (MINOR (CURRENT->dev) >> 2) + floppy_type;// 如果当前驱动器不是请求项中指定的驱动器,则置标志seek,表示需要进行寻道操作。// 然后置请求项设备为当前驱动器。 if (current_drive != CURRENT_DEV) seek = 1; current_drive = CURRENT_DEV;// 设置读写起始扇区。因为每次读写是以块为单位(1 块2 个扇区),所以起始扇区需要起码比// 磁盘总扇区数小2 个扇区。否则结束该次软盘请求项,执行下一个请求项。 block = CURRENT->sector; // 取当前软盘请求项中起始扇区号??block。 if (block + 2 > floppy->size) { // 如果block+2 大于磁盘扇区总数,则 end_request (0); // 结束本次软盘请求项。 goto repeat; }// 求对应在磁道上的扇区号,磁头号,磁道号,搜寻磁道号(对于软驱读不同格式的盘)。 sector = block % floppy->sect; // 起始扇区对每磁道扇区数取模,得磁道上扇区号。 block /= floppy->sect; // 起始扇区对每磁道扇区数取整,得起始磁道数。 head = block % floppy->head; // 起始磁道数对磁头数取模,得操作的磁头号。 track = block / floppy->head; // 起始磁道数对磁头数取整,得操作的磁道号。 seek_track = track << floppy->stretch; // 相应于驱动器中盘类型进行调整,得寻道号。// 如果寻道号与当前磁头所在磁道不同,则置需要寻道标志seek。 if (seek_track != current_track) seek = 1; sector++; // 磁盘上实际扇区计数是从1 算起。 if (CURRENT->cmd == READ) // 如果请求项中是读操作,则置软盘读命令码。 command = FD_READ; else if (CURRENT->cmd == WRITE) // 如果请求项中是写操作,则置软盘写命令码。 command = FD_WRITE; else panic ("do_fd_request: unknown command");// 添加定时器,用于指定驱动器到能正常运行所需延迟的时间(滴答数),当定时时间到时就调用// 函数floppy_on_interrupt(), add_timer (ticks_to_floppy_on (current_drive), &floppy_on_interrupt);}//// 软盘系统初始化。// 设置软盘块设备的请求处理函数(do_fd_request()),并设置软盘中断门(int 0x26,对应硬件// 中断请求信号IRQ6),然后取消对该中断信号的屏蔽,允许软盘控制器FDC 发送中断请求信号。voidfloppy_init (void){ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; // = do_fd_request()。 set_trap_gate (0x26, &floppy_interrupt); //设置软盘中断门 int 0x26(38)。 outb (inb_p (0x21) & ~0x40, 0x21); // 复位软盘的中断请求屏蔽位,允许// 软盘控制器发送中断请求信号。}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -