/*
* linux/kernel/floppy.c
*
* (C) 1991 Linus Torvalds
*/

/*
* 02.12.91 - Changed to static variables to indicate need for reset
* and recalibrate. This makes some things easier (output_byte reset
* checking etc), and means less interrupt jumping in case of errors,
* so the code is hopefully easier to understand.
*/
/*
* 02.12.91 - ޸ĳɾ̬ӦλУʹĳЩ
* Ϊ㣨output_byte λȣζڳʱжת
* ҪһЩϣܸױ⡣
*/

/*
* This file is certainly a mess. I've tried my best to get it working,
* but I don't like programming floppies, and I have only one anyway.
* Urgel. I should check for more errors, and do more graceful error
* recovery. Seems there are problems with several drives. I've tried to
* correct them. No promises.
*/
/*
* ļȻȽϻҡѾʹܹҲϲ̣
* Ҳֻһ⣬ӦĲԼĴ
* ĳЩ󻹴һЩ⡣ѾŽоˣܱ֤
* ʧ
*/

/*
* As with hd.c, all routines within this file can (and will) be called
* by interrupts, so extreme caution is needed. A hardware interrupt
* handler may not sleep, or a kernel panic will happen. Thus I cannot
* call "floppy-on" directly, but have to set a special timer interrupt
* etc.
*
* Also, I'm not certain this works on more than 1 floppy. Bugs may
* abund.
*/
/*
* ͬhd.c ļһļеӳܹжϵãҪر
* СġӲжϴǲ˯ߵģں˾ͻɵ()?˲
* ֱӵ"floppy-on"ֻһʱжϵȡ
*
* ⣬Ҳܱ֤óڶ1 ϵͳϹпܴڴ
*/

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/fs.h>		// ļϵͳͷļļṹfile,buffer_head,m_inode ȣ
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣
#include <linux/fdreg.h>	// ͷļ̿һЩ塣
#include <asm/system.h>		// ϵͳͷļû޸/жŵȵǶʽꡣ
#include <asm/io.h>		// io ͷļӲ˿/䡣
#include <asm/segment.h>	// βͷļйضμĴǶʽຯ

#define MAJOR_NR 2		// 豸2
#include "blk.h"		// 豸ͷļݽṹ豸ݽṹͺ꺯Ϣ

static int recalibrate = 0;	// ־ҪУ
static int reset = 0;		// ־Ҫиλ
static int seek = 0;		// Ѱ

extern unsigned char current_DOR;	// ǰĴ(Digital Output Register)

#define immoutb_p(val,port) \	// ֱֽǶԺ꣩
__asm__ ("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),
	 "i" (port))
// ڼ豸š豸 = TYPE*4 + DRIVE㷽μб
#define TYPE(x) ((x)>>2)	// ͣ2--1.2Mb7--1.44Mb
#define DRIVE(x) ((x)&0x03)	// ţ0--3 ӦA--D
/*
* Note that MAX_ERRORS=8 doesn't imply that we retry every bad read
* max 8 times - some types of errors increase the errorcount by 2,
* so we might actually retry only 5-6 times before giving up.
*/
/*
* ע⣬涨MAX_ERRORS=8 ʾÿζ8  - Щ
* Ĵ󽫰ѳֵ2ʵڷ֮ǰֻ賢5-6 鼴ɡ
*/
#define MAX_ERRORS 8
/*
* globals used by 'result()'
*/
/* Ǻ'result()'ʹõȫֱ */
// Щ״ֽ̬иλĺμinclude/linux/fdreg.h ͷļ
#define MAX_REPLIES 7		// FDC ෵7 ֽڵĽϢ
     static unsigned char reply_buffer[MAX_REPLIES];	// FDC صĽϢ
#define ST0 (reply_buffer[0])	// ؽ״ֽ̬0
#define ST1 (reply_buffer[1])	// ؽ״ֽ̬1
#define ST2 (reply_buffer[2])	// ؽ״ֽ̬2
#define ST3 (reply_buffer[3])	// ؽ״ֽ̬3

/*
* This struct defines the different floppy types. Unlike minix
* linux doesn't have a "search for right type"-type, as the code
* for that is convoluted and weird. I've got enough problems with
* this driver as it is.
*
* The 'stretch' tells if the tracks need to be boubled for some
* types (ie 360kB diskette in 1.2MB drive etc). Others should
* be self-explanatory.
*/
/*
* ̽ṹ˲ͬ͡minix ͬǣlinux û
* "ȷ"-ͣΪ䴦Ĵ˷ѽҹֵֹġ
* Ѿˡ
*
* ĳЩ͵̣1.2MB е360kB ̵ȣ'stretch'
* ŵǷҪ⴦Ӧġ
*/
// ̲У
// size С()
// sect ÿŵ
// head ͷ
// track ŵ
// stretch ԴŵǷҪ⴦־
// gap ϶(ֽ)
// rate ݴʣ
// spec1 4 λʣλͷжʱ䣩
     static struct floppy_struct
     {
       unsigned int size, sect, head, track, stretch;
       unsigned char gap, rate, spec1;
     }
floppy_type[] =
{
  {
  0, 0, 0, 0, 0, 0x00, 0x00, 0x00}
  ,				/* no testing */
  {
  720, 9, 2, 40, 0, 0x2A, 0x02, 0xDF}
  ,				/* 360kB PC diskettes */
  {
  2400, 15, 2, 80, 0, 0x1B, 0x00, 0xDF}
  ,				/* 1.2 MB AT-diskettes */
  {
  720, 9, 2, 40, 1, 0x2A, 0x02, 0xDF}
  ,				/* 360kB in 720kB drive */
  {
  1440, 9, 2, 80, 0, 0x2A, 0x02, 0xDF}
  ,				/* 3.5" 720kB diskette */
  {
  720, 9, 2, 40, 1, 0x23, 0x01, 0xDF}
  ,				/* 360kB in 1.2MB drive */
  {
  1440, 9, 2, 80, 0, 0x23, 0x01, 0xDF}
  ,				/* 720kB in 1.2MB drive */
  {
  2880, 18, 2, 80, 0, 0x1B, 0x00, 0xCF}
  ,				/* 1.44MB diskette */
};

/*
* Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
* Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
* H is head unload time (1=16ms, 2=32ms, etc)
*
 * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
 * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
 */
/*
* rate0 ʾ500kb/s1 ʾ300kbps2 ʾ250kbps
* spec1 0xSHS ǲʣF-1 룬E-2msD=3ms ȣ
* H Ǵͷжʱ䣨1=16ms2=32ms ȣ
*
* spec2 ǣHLD<<1 | NDHLD Ǵͷʱ䣨1=2ms2=4ms ȣ
* ND λʾʹDMANo DMAڳӲ6HLD=6msʹDMA
*/

extern void floppy_interrupt (void);
extern char tmp_floppy_area[1024];

 /*
    * These are global variables, as that's the easiest way to give
    * information to interrupts. They are the data used for the current
    * request.
  */
/*
* һЩȫֱΪǽϢжϳ򵥵ķʽ
* ڵǰݡ
*/
static int cur_spec1 = -1;
static int cur_rate = -1;
static struct floppy_struct *floppy = floppy_type;
static unsigned char current_drive = 0;
static unsigned char sector = 0;
static unsigned char head = 0;
static unsigned char track = 0;
static unsigned char seek_track = 0;
static unsigned char current_track = 255;
static unsigned char command = 0;
unsigned char selected = 0;
struct task_struct *wait_on_floppy_select = NULL;

//// ͷţȡѡģ̣
// Ĵ(DOR)ĵ2 λָѡ0-3 ӦA-D
void
floppy_deselect (unsigned int nr)
{
  if (nr != (current_DOR & 3))
    printk ("floppy_deselect: drive not selected\n\r");
  selected = 0;
  wake_up (&wait_on_floppy_select);
}

 /*
    * floppy-change is never called from an interrupt, so we can relax a bit
    * here, sleep etc. Note that floppy-on tries to set current_DOR to point
    * to the desired drive, but it will probably not survive the sleep if
    * several floppies are used at the same time: thus the loop.
  */
/*
* floppy-change()Ǵжϳеõģǿһ£˯ȡ
* עfloppy-on()᳢current_DOR ָͬʱʹü
* ʱ˯ߣ˴ʱֻʹѭʽ
*/
//// ̸̸ָ򷵻1򷵻0
int
floppy_change (unsigned int nr)
{
repeat:
  floppy_on (nr);		// ָnrkernel/sched.c,251
// ǰѡָnrѾѡõǰж
// ȴ״̬
  while ((current_DOR & 3) != nr && selected)
    interruptible_sleep_on (&wait_on_floppy_select);
// ǰûѡߵǰ񱻻ʱǰȻָnrѭȴ
  if ((current_DOR & 3) != nr)
    goto repeat;
// ȡĴֵλλ7λʾѸʱرﲢ˳1
// ر˳0
  if (inb (FD_DIR) & 0x80)
    {
      floppy_off (nr);
      return 1;
    }
  floppy_off (nr);
  return 0;
}

//// ڴ顣
#define copy_buffer(from,to) \
 __asm__( "cld ; rep ; movsl" \
 :: "c" (BLOCK_SIZE/4), "S" ((long)(from)), "D" ((long)(to)) \
 : "cx", "di", "si")

//// ãʼDMA ͨ
static void
setup_DMA (void)
{
  long addr = (long) CURRENT->buffer;	// ǰڴλãַ

  cli ();
// ڴ1M ϵĵطDMA ʱ(tmp_floppy_area )
// (Ϊ8237A оƬֻ1M ַΧѰַ)д轫ݸƵʱ
  if (addr >= 0x100000)
    {
      addr = (long) tmp_floppy_area;
      if (command == FD_WRITE)
	copy_buffer (CURRENT->buffer, tmp_floppy_area);
    }
/* mask DMA 2 *//* DMA ͨ2 */
// ͨμĴ˿Ϊ0x10λ0-1 ָDMA ͨ(0--3)λ21 ʾΣ0 ʾ
  immoutb_p (4 | 2, 10);
  /* output command byte. I don't know why, but everyone (minix, */
  /* sanches & canton) output this twice, first to 12 then to 11 */
/* ֽڡǲ֪Ϊʲôÿˣminix*/
/* sanches cantonΣ12 ڣȻ11  */
// ǶDMA ˿12 11 дʽ֣0x46д0x4A
  __asm__ ("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
	   "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
	   "a" ((char) ((command == FD_READ) ? DMA_READ : DMA_WRITE)));
/* 8 low bits of addr *//* ַ0-7 λ */
// DMA ͨ2 д/ǰַĴ˿4
  immoutb_p (addr, 4);
  addr >>= 8;
/* bits 8-15 of addr *//* ַ8-15 λ */
  immoutb_p (addr, 4);
  addr >>= 8;
/* bits 16-19 of addr *//* ַ16-19 λ */
// DMA ֻ1M ڴռѰַ16-19 λַҳĴ(˿0x81)
  immoutb_p (addr, 0x81);
/* low 8 bits of count-1 (1024-1=0x3ff) *//* 8 λ(1024-1=0x3ff) */
// DMA ͨ2 д/ǰֽڼֵ˿5
  immoutb_p (0xff, 5);
/* high 8 bits of count-1 *//* 8 λ */
// һι1024 ֽڣ
  immoutb_p (3, 5);
/* activate DMA 2 *//* DMA ͨ2  */
// λDMA ͨ2 ΣDMA2 DREQ źš
  immoutb_p (0 | 2, 10);
  sti ();
}

//// ̿һֽݣ
static void
output_byte (char byte)
{
  int counter;
  unsigned char status;

  if (reset)
    return;
// ѭȡ״̬FD_STATUS(0x3f4)״̬״̬STATUS_READY STATUS_DIR=0
// (CPU??FDC)ݶ˿ָֽڡ
  for (counter = 0; counter < 10000; counter++)
    {
      status = inb_p (FD_STATUS) & (STATUS_READY | STATUS_DIR);
      if (status == STATUS_READY)
	{
	  outb (byte, FD_DATA);
	  return;
	}
    }
// ѭ1 νܷͣøλ־ӡϢ
  reset = 1;
  printk ("Unable to send byte to FDC\n\r");
}

//// ȡFDC ִеĽϢ
// Ϣ7 ֽڣreply_buffer[]СضĽֵֽ=-1
// ʾ
static int
result (void)
{
  int i = 0, counter, status;

  if (reset)
    return -1;
  for (counter = 0; counter < 10000; counter++)
    {
      status = inb_p (FD_STATUS) & (STATUS_DIR | STATUS_READY | STATUS_BUSY);
      if (status == STATUS_READY)
	return i;
      if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY))
	{
	  if (i >= MAX_REPLIES)
	    break;
	  reply_buffer[i++] = inb_p (FD_DATA);
	}
    }
  reset = 1;
  printk ("Getstatus times out\n\r");
  return -1;
}

//// ̲жϵúжϴá
static void
bad_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 void
rw_interrupt (void)
{
// ؽֽ7״ֽ̬01 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 void
setup_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 void
seek_interrupt (void)
{
/* sense drive status *//* ж״̬ */
// ͼж״̬ؽϢֽڣST0 ʹͷǰŵš
  output_byte (FD_SENSEI);
// ؽֽ2ST0 ΪѰߴͷڴŵ(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 void
transfer (void)
{
// ȿǰǷָĲǾͷӦ
// 14 λʣλͷжʱ䣻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 void
recal_interrupt (void)
{
  output_byte (FD_SENSEI);	// ͼж״̬
  if (result () != 2 || (ST0 & 0xE0) == 0x60)	// ؽֽ2 
    reset = 1;			// 쳣øλ־
  else				// λУ־
    recalibrate = 0;
  do_fd_request ();		// ִ
}

//// жжϵú
// ȷͼж״̬޲ؽøλ־
// У־
void
unexpected_floppy_interrupt (void)
{
  output_byte (FD_SENSEI);	// ͼж״̬
  if (result () != 2 || (ST0 & 0xE0) == 0x60)	// ؽֽ2 
    reset = 1;			// 쳣øλ־
  else				// У־
    recalibrate = 1;
}

//// У
// ̿FDC УͲλУ־
static void
recalibrate_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 void
reset_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 void
reset_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 void
floppy_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 ();		// ̶ִд亯
}

//// ̶д
//
void
do_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 жźš
void
floppy_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);	// λ̵жλ
// ̿жźš
}
