atapi.c

来自「一个类似windows」· C语言 代码 · 共 2,047 行 · 第 1/5 页

C
2,047
字号
  UCHAR High;
  UCHAR Low;

  DPRINT("AtapiFindDevices() called\n");

  CommandPortBase = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[0].RangeStart);
  DPRINT("  CommandPortBase: %x\n", CommandPortBase);

  ControlPortBase = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[1].RangeStart);
  DPRINT("  ControlPortBase: %x\n", ControlPortBase);

  for (UnitNumber = 0; UnitNumber < 2; UnitNumber++)
    {
      /* Select drive */
      IDEWriteDriveHead(CommandPortBase,
			IDE_DH_FIXED | (UnitNumber ? IDE_DH_DRV1 : 0));
      ScsiPortStallExecution(500);

      /* Disable interrupts */
      IDEWriteDriveControl(ControlPortBase,
			   IDE_DC_nIEN);
      ScsiPortStallExecution(500);

      /* Check if a device is attached to the interface */
      IDEWriteCylinderHigh(CommandPortBase, 0xaa);
      IDEWriteCylinderLow(CommandPortBase, 0x55);

      High = IDEReadCylinderHigh(CommandPortBase);
      Low = IDEReadCylinderLow(CommandPortBase);

      IDEWriteCylinderHigh(CommandPortBase, 0);
      IDEWriteCylinderLow(CommandPortBase, 0);

      if (Low != 0x55 || High != 0xaa)
	{
	  DPRINT("No Drive found. UnitNumber %d CommandPortBase %x\n", UnitNumber, CommandPortBase);
	  continue;
	}

      AtapiExecuteCommand(DeviceExtension, IDE_CMD_RESET, NULL);

      for (Retries = 0; Retries < 20000; Retries++)
	{
	  if (!(IDEReadStatus(CommandPortBase) & IDE_SR_BUSY))
	    {
	      break;
	    }
	  ScsiPortStallExecution(150);
	}
      if (Retries >= 20000)
	{
	  DPRINT("Timeout on drive %lu\n", UnitNumber);
	  DeviceExtension->DeviceFlags[UnitNumber] &= ~DEVICE_PRESENT;
	  continue;
	}

      High = IDEReadCylinderHigh(CommandPortBase);
      Low = IDEReadCylinderLow(CommandPortBase);

      DPRINT("  Check drive %lu: High 0x%x Low 0x%x\n",
	     UnitNumber,
	     High,
	     Low);

      if (High == 0xEB && Low == 0x14)
	{
	  if (AtapiIdentifyDevice(CommandPortBase,
				  ControlPortBase,
				  UnitNumber,
				  TRUE,
				  &DeviceExtension->DeviceParams[UnitNumber]))
	    {
	      DPRINT("  ATAPI drive found!\n");
	      DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_PRESENT;
	      DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_ATAPI;
	      DeviceExtension->TransferSize[UnitNumber] =
		DeviceExtension->DeviceParams[UnitNumber].BytesPerSector;
#ifdef ENABLE_DMA
              if (AtapiConfigDma(DeviceExtension, UnitNumber))
	        {
		  DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_DMA_CMD;
		}
#endif
              if (!(DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x1000) ||
		  !(DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x1000))
                {
                  DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH;                      
                }

              /* Don't flush CD/DVD drives */
              if (((DeviceExtension->DeviceParams[UnitNumber].ConfigBits >> 8) & 0x1F) == READ_ONLY_DIRECT_ACCESS_DEVICE)
	        {
	          DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH;
	        }
	      DeviceFound = TRUE;
	    }
	  else
	    {
	      DPRINT("  No ATAPI drive found!\n");
	    }
	}
      else
	{
	  if (AtapiIdentifyDevice(CommandPortBase,
				  ControlPortBase,
				  UnitNumber,
				  FALSE,
				  &DeviceExtension->DeviceParams[UnitNumber]))
	    {
	      DPRINT("  IDE drive found!\n");
	      DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_PRESENT;
	      DeviceExtension->TransferSize[UnitNumber] = DeviceExtension->DeviceParams[UnitNumber].BytesPerSector;
	      if ((DeviceExtension->DeviceParams[UnitNumber].RWMultImplemented & 0x8000) &&
		  (DeviceExtension->DeviceParams[UnitNumber].RWMultImplemented & 0xff) &&
		  (DeviceExtension->DeviceParams[UnitNumber].RWMultCurrent & 0x100) &&
		  (DeviceExtension->DeviceParams[UnitNumber].RWMultCurrent & 0xff))
		{
		  DeviceExtension->TransferSize[UnitNumber] *= (DeviceExtension->DeviceParams[UnitNumber].RWMultCurrent & 0xff);
		  DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_MULTI_SECTOR_CMD;
		}
	      if (DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x0400 &&
		  DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x0400)
	        {
		  DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_48BIT_ADDRESS;
		}
	      if (DeviceExtension->DeviceParams[UnitNumber].DWordIo)
		{
		  DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_DWORD_IO;
		}
#ifdef ENABLE_DMA
	      if (AtapiConfigDma(DeviceExtension, UnitNumber))
	        {
		  DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_DMA_CMD;
		}
#endif
              if (DeviceExtension->DeviceFlags[UnitNumber] & DEVICE_48BIT_ADDRESS)
                {
                  if (!(DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x2000) ||
		      !(DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x2000))
                    {
                      DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH;                      
                    }
                }
              else
                {
                  if (!(DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x1000) ||
		      !(DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x1000))
                    {
                      DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH;                      
                    }
                }
	      DeviceFound = TRUE;
	    }
	  else
	    {
	      DPRINT("  No IDE drive found!\n");
	    }
	}
    }

  /* Reset pending interrupts */
  IDEReadStatus(CommandPortBase);
  /* Reenable interrupts */
  IDEWriteDriveControl(ControlPortBase, 0);
  ScsiPortStallExecution(500);
  /* Return with drive 0 selected */
  IDEWriteDriveHead(CommandPortBase, IDE_DH_FIXED);
  ScsiPortStallExecution(500);

  DPRINT("AtapiFindDrives() done (DeviceFound %s)\n", (DeviceFound) ? "TRUE" : "FALSE");

  return(DeviceFound);
}


/*
 *  AtapiIdentifyDevice
 *
 *  DESCRIPTION:
 *	Get the identification block from the drive
 *
 *  RUN LEVEL:
 *	PASSIVE_LEVEL
 *
 *  ARGUMENTS:
 *	CommandPort
 *		Address of the command port
 *	ControlPort
 *		Address of the control port
 *	DriveNum
 *		The drive index (0,1)
 *	Atapi
 *		Send an ATA(FALSE) or an ATAPI(TRUE) identify comand
 *	DrvParms
 *		Address to write drive ident block
 *
 *  RETURNS:
 *	TRUE: The drive identification block was retrieved successfully
 *	FALSE: an error ocurred
 */

static BOOLEAN
AtapiIdentifyDevice(IN ULONG CommandPort,
		    IN ULONG ControlPort,
		    IN ULONG DriveNum,
		    IN BOOLEAN Atapi,
		    OUT PIDE_DRIVE_IDENTIFY DrvParms)
{
  LONG i;
  ULONG mode;
  char SerialNumber[20];
  char FirmwareRev[8];
  char ModelNumber[40];

  /*  Get the Drive Identify block from drive or die  */
  if (AtapiPolledRead(CommandPort,
		      ControlPort,
		      0,
		      1,
		      0,
		      0,
		      0,
		      (DriveNum ? IDE_DH_DRV1 : 0),
		      (Atapi ? IDE_CMD_IDENT_ATAPI_DRV : IDE_CMD_IDENT_ATA_DRV),
		      (PUCHAR)DrvParms) == FALSE)
    {
      DPRINT("AtapiPolledRead() failed\n");
      return FALSE;
    }

  /*  Report on drive parameters if debug mode  */
  memcpy(SerialNumber, DrvParms->SerialNumber, 20);
  memcpy(FirmwareRev, DrvParms->FirmwareRev, 8);
  memcpy(ModelNumber, DrvParms->ModelNumber, 40);
  IDESwapBytePairs((PUCHAR)SerialNumber, 20);
  IDESwapBytePairs((PUCHAR)FirmwareRev, 8);
  IDESwapBytePairs((PUCHAR)ModelNumber, 40);
  DPRINT("Config:%04x  Cyls:%5d  Heads:%2d  Sectors/Track:%3d  Gaps:%02d %02d\n",
         DrvParms->ConfigBits,
         DrvParms->LogicalCyls,
         DrvParms->LogicalHeads,
         DrvParms->SectorsPerTrack,
         DrvParms->InterSectorGap,
         DrvParms->InterSectorGapSize);
  DPRINT("Bytes/PLO:%3d  Vendor Cnt:%2d  Serial number:[%.20s]\n",
         DrvParms->BytesInPLO,
         DrvParms->VendorUniqueCnt,
         SerialNumber);
  DPRINT("Cntlr type:%2d  BufSiz:%5d  ECC bytes:%3d  Firmware Rev:[%.8s]\n",
         DrvParms->ControllerType,
         DrvParms->BufferSize * IDE_SECTOR_BUF_SZ,
         DrvParms->ECCByteCnt,
         FirmwareRev);
  DPRINT("Model:[%.40s]\n", ModelNumber);
  DPRINT("RWMultMax?:%04x  RWMult?:%02x  LBA:%d  DMA:%d  MinPIO:%d ns  MinDMA:%d ns\n",
         (DrvParms->RWMultImplemented),
	 (DrvParms->RWMultCurrent) & 0xff,
         (DrvParms->Capabilities & IDE_DRID_LBA_SUPPORTED) ? 1 : 0,
         (DrvParms->Capabilities & IDE_DRID_DMA_SUPPORTED) ? 1 : 0,
         DrvParms->MinPIOTransTime,
         DrvParms->MinDMATransTime);
  DPRINT("TM:Cyls:%d  Heads:%d  Sectors/Trk:%d Capacity:%ld\n",
         DrvParms->TMCylinders,
         DrvParms->TMHeads,
         DrvParms->TMSectorsPerTrk,
         (ULONG)(DrvParms->TMCapacityLo + (DrvParms->TMCapacityHi << 16)));
  DPRINT("TM:SectorCount: 0x%04x%04x = %lu\n",
         DrvParms->TMSectorCountHi,
         DrvParms->TMSectorCountLo,
         (ULONG)((DrvParms->TMSectorCountHi << 16) + DrvParms->TMSectorCountLo));
  DPRINT("SupportedFeatures83: %x, EnabledFeatures86 %x\n", DrvParms->SupportedFeatures83, DrvParms->EnabledFeatures86);
  DPRINT("Max48BitAddress: %I64d\n", *(PULONGLONG)DrvParms->Max48BitAddress);
  if (DrvParms->TMFieldsValid & 0x0004)
    {
      if ((DrvParms->UltraDmaModes >> 8) && (DrvParms->UltraDmaModes & 0xff))
        {
	  mode = 7;
	  while (!(DrvParms->UltraDmaModes & (0x0100 << mode)))
	    {
	      mode--;
	    }
	  DPRINT("Ultra DMA mode %d is selected\n", mode);
	}
      else if ((DrvParms->MultiDmaModes >> 8) & (DrvParms->MultiDmaModes & 0x07))
        {
	  mode = 2;
	  while(!(DrvParms->MultiDmaModes & (0x01 << mode)))
	    {
	      mode--;
	    }
	  DPRINT("Multi DMA mode %d is selected\n", mode);
	}
    }

  if (! Atapi && 0 != (DrvParms->Capabilities & IDE_DRID_LBA_SUPPORTED))
    {
      /* LBA ATA drives always have a sector size of 512 */
      DrvParms->BytesPerSector = 512;
    }
  else
    {
      DPRINT("BytesPerSector %d\n", DrvParms->BytesPerSector);
      if (DrvParms->BytesPerSector == 0)
        {
          DrvParms->BytesPerSector = 512;
        }
      else
        {
          for (i = 15; i >= 0; i--)
            {
              if (DrvParms->BytesPerSector & (1 << i))
                {
                  DrvParms->BytesPerSector = 1 << i;
                  break;
                }
            }
        }
    }
  DPRINT("BytesPerSector %d\n", DrvParms->BytesPerSector);

  return TRUE;
}


//    AtapiPolledRead
//
//  DESCRIPTION:
//    Read a sector of data from the drive in a polled fashion.
//
//  RUN LEVEL:
//    PASSIVE_LEVEL
//
//  ARGUMENTS:
//    IN   ULONG  CommandPort   Address of command port for drive
//    IN   ULONG  ControlPort   Address of control port for drive
//    IN   UCHAR  PreComp       Value to write to precomp register
//    IN   UCHAR  SectorCnt     Value to write to sectorCnt register
//    IN   UCHAR  SectorNum     Value to write to sectorNum register
//    IN   UCHAR  CylinderLow   Value to write to CylinderLow register
//    IN   UCHAR  CylinderHigh  Value to write to CylinderHigh register
//    IN   UCHAR  DrvHead       Value to write to Drive/Head register
//    IN   UCHAR  Command       Value to write to Command register
//    OUT  PUCHAR Buffer        Buffer for output data
//
//  RETURNS:
//    BOOLEAN: TRUE success, FALSE error
//

static BOOLEAN
AtapiPolledRead(IN ULONG CommandPort,
		IN ULONG ControlPort,
		IN UCHAR PreComp,
		IN UCHAR SectorCnt,
		IN UCHAR SectorNum,
		IN UCHAR CylinderLow,
		IN UCHAR CylinderHigh,
		IN UCHAR DrvHead,
		IN UCHAR Command,
		OUT PUCHAR Buffer)
{
  ULONG SectorCount = 0;
  ULONG RetryCount;
  BOOLEAN Junk = FALSE;
  UCHAR Status;

  /* Wait for BUSY to clear */
  for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++)
    {
      Status = IDEReadStatus(CommandPort);
      if (!(Status & IDE_SR_BUSY))
        {
          break;
        }
      ScsiPortStallExecution(10);
    }
  DPRINT("status=%02x\n", Status);
  DPRINT("waited %ld usecs for busy to clear\n", RetryCount * 10);
  if (RetryCount >= IDE_MAX_BUSY_RETRIES)
    {
      DPRINT("Drive is BUSY for too long\n");
      return FALSE;
    }

  /*  Write Drive/Head to select drive  */
  IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead);
  ScsiPortStallExecution(500);

  /* Disable interrupts */
  IDEWriteDriveControl(ControlPort, IDE_DC_nIEN);
  ScsiPortStallExecution(500);

#if 0
  /*  Wait for STATUS.BUSY and STATUS.DRQ to clear  */
  for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++)
    {
      Status = IDEReadStatus(CommandPort);
      if (!(Status & IDE_SR_BUSY) && !(Status & IDE_SR_DRQ))
	{
	  break;
	}
      ScsiPortStallExecution(10);
    }
  if (RetryCount >= IDE_MAX_BUSY_RETRIES)
    {
      return FALSE;
    }
#endif

  /*  Issue command to drive  */
  if (DrvHead & IDE_DH_LBA)

⌨️ 快捷键说明

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