⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hdd.c

📁 MMURTL(tm) Computer Operating System Ver x0.8, source code.
💻 C
📖 第 1 页 / 共 3 页
字号:
*/

/* reset the HDC - hd_reset resets the controller (which controlls
   both drives). We have to do it once, then try both physical drives.
   If the second drive is not there, some controllers will lock-up
   (the el-cheapos).  In this case we have to reset it again so it
   will work.  It seems like a lot of work, but to make it function
   with the widest range of IDE and MFM controllers this is the
   only way I have found that works.
*/

  hd_reset();		/* no error is returned */

/* Now we attempt to select and recal both drives.
   The driver MUST be able to recal the first physical drive
   or the Driver won't install.
*/

	erc = hd_recal(0);			/* try to recal */
	if (erc) {					/* try one more time! */
		hd_reset();
		erc = hd_recal(0);			/* try to recal */
		if (erc) {
	        hdcb[0].last_erc = erc;
			hd0_type = 0;			/* Must not be a valid drive */
			return(ErcNoDrive0);
	 	}
    }


	/* if we got here, drive 0 looks OK and the controller is
	   functioning.  Now we try drive 1 if type > 0.
	*/

	if (hd1_type) {
		erc = hd_recal(1);	/* try to recal if CMOS says it's there */
		if (erc) {
	        hdcb[1].last_erc = erc;
			hd1_type = 0;			/* Guess it's not a valid drive */

			if (!erc)

		/* We must redo drive 0 cause some cheap controllers lockup
		on us if drive 1 is not there.  They SHOULD simply return
		a Bad Command bit set in the Error register, but they don't. */

			hd_reset();
			erc = hd_recal(0);			/* recal drive 0 */
   	     hdcb[0].last_erc = erc;
		}
	}

 return(erc = InitDevDr(12, &hdcb, 2, 1));

}

/************************************************************
 Reset the HD controller.  This should only be called by
 DeviceInit or hdisk_setup.  This resets the controller
 and reloads parameters for both drives (if present) and
 attempts to recal them.
*************************************************************/

void hd_reset(void)
{
U32 i;
	UnMaskIRQ(14);      		/*  enable the IRQ */
	OutByte(4, HD_REG_PORT); 	/*  reset the controller */
   	MicroDelay(4);  			/*  Delay 60us */

    /* bit 3 of HD_REG must be 1 for access to heads 8-15 */
    /* Clear "MUCHO" heads bit, and clear the reset bit */

    OutByte(hd_control & 0x0f, HD_REG_PORT);

	Sleep(20);		/* 200ms - seems some controllers are SLOW!! */
    i = CheckMsg(hd_exch, &hd_msg); 	/* Eat Int if one came back  */

	hdstatus.ResetStatByte = statbyte;	/* The ISR gets statbyte */

	if (i) hdstatus.fIntOnReset = 1;
	else hdstatus.fIntOnReset = 0;

}

/*************************************************************
 The ISR is VERY simple. It just waits for an interrupt, gets
 the single status byte from the controller (which clears the
 interrupt condition) then sends an empty message to the
 exchange where the HD Driver task will be waiting.
 This tells the HD task currently running that it's got
 some status to act on!
****************************************************************/
void interrupt hdisk_isr(void)
{
	statbyte = InByte(HD_PORT+7);
    HDDInt = 1;
	ISendMsg(hd_exch, 0xfffffff0, 0xfffffff0);
	EndOfIRQ(14);
}

/*************************************************************
 This checks the HDC controller to see if it's busy so we can
 send it commands or read the rest of the registers.
 We will wait up to 3 seconds then error out.
 The caller should call check_busy and check the error.
 If it's 0 then the controller became ready in less than
 3 seconds.  ErcNotReady will be returned otherwise.
 It leaves the status byte in the global statbyte.
****************************************************************/

U32  check_busy(void)
{
S16 count;

  count = 0;
  while (count++ < 60) {
	statbyte = InByte(HD_PORT+7);
	if ((statbyte & BUSY) == 0) return(ok);
	Sleep(5);  /* 50ms shots */
	}
  return(ErcNotReady);	 /* controller out to lunch! */
}

/*************************************************************
  This sends the SetParams command to the controller to set
  up the drive geometry (nHeads, nSectors, etc.).
****************************************************************/

U32  hd_init(U8 drive)
{
U32 erc;
  /* set max heads, sectors and cylinders */
  if (drive == 0) {								/* Drive 0 */
    hd_Cmd[2] = hd0_secpertrk;					/* sector count */
    hd_Cmd[6] = (drive << 4) | ((hd0_heads-1) & 0x0f) | 0xa0; /* hds & drv */
	}
  else {										/* Drive 1 */
    hd_Cmd[2] = hd1_secpertrk;					/* sector count */
    hd_Cmd[6] = (drive << 4) | ((hd1_heads-1) & 0x0f) | 0xa0; /* hds & drv */
	}
  hd_Cmd[1] = 0;
  hd_Cmd[3] = 0;
  hd_Cmd[4] = 0;								/* cyl = 0 for init */
  hd_Cmd[5] = 0;								/* cyl = 0 for init */

  erc = send_command(HDC_SET_PARAMS);		/* Send the command */
  erc = hd_wait();							/* wait for interrupt */
  if (!erc)
      erc = hd_status(HDC_SET_PARAMS);
  return(erc);
}

/******************************************
Wait for the hardware interrupt to occur.
Time-out and return if no interrupt.
********************************************/

U32  hd_wait(void)
{
U32  erc;
U8 fMsg;

/* Set alarm for 3 seconds */

HDDInt = 0;
KillAlarm(hd_exch);			/* kill any pending alarm */

erc = Alarm(hd_exch, 300);	/* Set it up again */
if (erc)
	return(erc);		/* bad problem */

/* We call check in a loop with a small sleep in between because
the ISR is using ISend which doesn't force a Task Switch */

/*
fMsg = 0;
while (!fMsg) {
	erc = CheckMsg(hd_exch, &hd_msg);
	if (!erc)
		fMsg = 1;
	else if (erc == ErcNoMsg)
		Sleep(2);
	else {
		KillAlarm(hd_exch);
		return (erc);
	}
}
*/

erc = WaitMsg(hd_exch, &hd_msg);

KillAlarm(hd_exch);

if (hd_msg != 0xfffffff0)  {        /* HD interrupt sends fffffff0 */
    if (HDDInt)
      return(ErcMissHDDInt);
	else
		return(ErcHDCTimeOut);		/* Alarm sends 0xffffffff      */
	}
else {
	KillAlarm(hd_exch);
	return(ok);
	}
}

/********************************************
    Recalibrate the drive.
*********************************************/

U32  hd_recal(U8 drive)
{
U32 erc;
  hd_Cmd[6] = (drive << 4) | (hd_head & 0x0f) | 0xa0;
  erc = send_command(HDC_RECAL);
  if (!erc)
    erc = hd_wait();						/* wait for interrupt */
  if (!erc)
      erc = hd_status(HDC_RECAL);
  if (drive)
    hdstatus.LastRecalErc1 = erc;
  else
    hdstatus.LastRecalErc0 = erc;
  return(erc);
}


/********************************************
    Send the command to the controller.
    Clear the Echange of any left over
    alarm or int messages before we
    send a command.
*********************************************/

U32  send_command(U8  Cmd)
{
U32 erc, msg[2];

	while (CheckMsg(hd_exch, &msg) == 0);	/* Empty it */

    /* bit 3 of HD_REG must be 1 for access to heads 8-15 */
    if (hd_head > 7) {
      hd_control |= 0x08;
      OutByte(hd_control, HD_REG_PORT); 	/*  set bit for head > 7 */
      hd_control &= 0xf7;
      }
    erc = check_busy();
    if (!erc) OutByte(hd_Cmd[1], HD_PORT+1);
    if (!erc) erc = check_busy();
    if (!erc) OutByte(hd_Cmd[2], HD_PORT+2);
    if (!erc) erc = check_busy();
    if (!erc) OutByte(hd_Cmd[3], HD_PORT+3);
    if (!erc) erc = check_busy();
    if (!erc) OutByte(hd_Cmd[4], HD_PORT+4);
    if (!erc) erc = check_busy();
    if (!erc) OutByte(hd_Cmd[5], HD_PORT+5);
    if (!erc) erc = check_busy();
    if (!erc) OutByte(hd_Cmd[6], HD_PORT+6);
    if (!erc) erc = check_busy();
    if (!erc) OutByte(Cmd, HD_PORT+7);
    return(erc);
}

/*************************************************************
 This sets up the cylinder, head and sector variables for all
 commands that require them (read, write, verify, format, seek).
 nBlks ca NOT be greater than the hardware can handle. For
 IDE/MFM controllers this is 128 sectors.
 The caculated values are placed in the proper command byte
 in anticipation of the command being sent.
*************************************************************/

U32  setupseek(U32 dLBA, U32 nBlks)
{
 U32  j;
 U16  cyl;

  if (nBlks > 256) return ErcTooManyBlks;
  if (nBlks == 0) return ErcZeroBlks;

  hd_nsectors = nBlks;
  if (hd_nsectors == 256) hd_nsectors = 0;   /* 0==256 for controller */

  if (hd_drive == 0) {		/* drive 0 */

	cyl = dLBA / (hd0_heads * hd0_secpertrk);
 	j = dLBA % (hd0_heads * hd0_secpertrk);		/* remainder */

    /* we now know what cylinder, calculate head and sector */

    hd_head = j / hd0_secpertrk;
    hd_sector = j % hd0_secpertrk + 1; /* sector number start at 1 !!! */

  }
  else {						/* drive 1 */

	cyl = dLBA / (hd1_heads * hd1_secpertrk);
 	j = dLBA % (hd1_heads * hd1_secpertrk);		/* remainder */

    /* we now know what cylinder, calculate head and sector */

    hd_head = j / hd1_secpertrk;
    hd_sector = j % hd1_secpertrk + 1; /* sector number start at 1 !!! */
  }

  hd_Cmd[2] = nBlks;					/* How many sectors */
  hd_Cmd[3] = hd_sector;				/* Which sector to start on */
  hd_Cmd[4] = cyl & 0xff;				/* cylinder lobyte */
  hd_Cmd[5] = (cyl >> 8) & 0xff;		/* cylinder hibyte */
  hd_Cmd[6] = (hd_drive << 4) | (hd_head & 0x0f) | 0xa0;

  return ok;
}


/*******************************************************
  Move the head to the selected track (cylinder).
*********************************************************/

U32  hd_seek(U32 dLBA)
{
U32 erc;
  erc = setupseek(dLBA, 1);					/* sets up for implied seek */
  if (!erc) erc = send_command(HDC_SEEK);	/* Not implied anymore... */
  if (!erc) erc = hd_wait();				/* wait for interrupt */
  if (!erc) erc = hd_status(HDC_SEEK);
  hdstatus.LastSeekErc0 = erc;
  return(erc);
}


/*******************************************************
 Called to read status and errors from the controller
 after an interrupt generated by a command we sent.
 The error checking is based on the command that we sent.
 This is done because certain bits in the status and error
 registers are actually not errors, but simply indicate
 status or indicate an action we must take next.
 ZERO returned indicates no errors for the command status
 we are checking.
*********************************************************/

U32  hd_status(U8  LastCmd)
{
U32 erc;
U8  statbyte, errbyte;

  /* We shouldn't see the controller busy. After all,
     he interrupted us with status.
  */

  erc = check_busy();		/* puts status byte into global StatByte */
  if (!erc)
    statbyte = InByte(HD_PORT+7);
  else return(erc);

  if (hd_drive)
	  hdstatus.LastStatByte1 = statbyte;
  else
	  hdstatus.LastStatByte0 = statbyte;

  if ((statbyte & ERROR) == 0) {		/* Error bit not set in status reg */
    erc = ok;  /* default */

	switch (LastCmd) {
	  case HDC_READ:
	  case HDC_READ_LONG:
	  case HDC_WRITE:
	  case HDC_WRITE_LONG:
	  case HDC_SEEK:
	  case HDC_RECAL:
		if (statbyte & WRITE_FAULT) erc = ErcWriteFault;
		else

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -