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

📄 ide_drive.cxx

📁 C++ 编写的EROS RTOS
💻 CXX
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* TODO: This interface won't work with older ST506 or ESDI drives. * At the moment I have no plans to support such drives.  Ever.  I'm * probably the only person on the face of the planet who still owns * any that work :-).  Even the LINUX crew are now compiling those * drives out by default. *//* #define VERBOSE_BUG */#include <kerninc/kernel.hxx>#include <kerninc/MsgLog.hxx>#include <kerninc/AutoConf.hxx>#include <kerninc/IoRegion.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/Machine.hxx>#include <kerninc/SysTimer.hxx>#include <kerninc/BlockDev.hxx>#include <kerninc/IoRequest.hxx>#ifdef VERBOSE_BUG#include <arch/i486/kernel/lostart.hxx>#endif#include <arch/i486/kernel/SysConfig.hxx>#include <arch/i486/kernel/CMOS.hxx>#include "io.h"#include "ide_ide.hxx"#include "ide_drive.hxx"#include "ide_hwif.hxx"#include "ide_group.hxx"	      #define dbg_probe	0x1	/* steps in taking snapshot *//* Following should be an OR of some of the above */#define dbg_flags   ( 0u )#define DBCOND(x) (dbg_##x & dbg_flags)#define DEBUG(x) if DBCOND(x)#define DEBUG2(x,y) if ((dbg_##x|dbg_##y) & dbg_flags)/* #define IDE_DEBUG */#define INITIAL_MULT_COUNT 8	/* 4K per xfer max */#define IS_PROMISE_DRIVE 0	/* for now */#define FANCY_STATUS_DUMPS/* * Probably not wise to fiddle with these */#define ERROR_MAX	8	/* Max read/write errors per sector */#define ERROR_RESET	3	/* Reset controller every 4th retry */#define ERROR_RECAL	1	/* Recalibrate every 2nd retry */#define DRIVE_SELECT_DELAY 10	/* in MILLISECONDS */#define DRIVE_DETECT_DELAY 50ide_drive::ide_drive(){  present = false;  mounted = false;  needsMount = false;  removable = false;  media = IDE::med_disk;  ctl = 0x08;#if 0  p_chs.heads = 0;  p_chs.secs = 0;  p_chs.cyls = 0;#endif  l_chs.hd = 0;  l_chs.sec = 0;  l_chs.cyl = 0;  b_chs.hd = 0;  b_chs.sec = 0;  b_chs.cyl = 0;  select.all = 0xa0;  flags.all = 0;  flags.b.recal = 1; /* seek to track 0 */  flags.b.setGeom = 1; /* tell it its geometry */  id = 0;  name[0] = 'h';  name[1] = 'd';  name[2] = '?';  name[3] = '?';  name[4] = 0;}/* * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity" * value for this drive (from its reported identification information). * * Returns:	true if lba_capacity looks sensible *		false otherwise */bool ide_driveid::CheckLbaCapacity(){  uint32_t lba_sects   = lba_capacity;  uint32_t chs_sects   = cyls * heads * sectors;  uint32_t _10_percent = chs_sects / 10;  /* perform a rough sanity check on lba_sects:  within 10% is "okay" */  if ((lba_sects - chs_sects) < _10_percent)    return true;		/* lba_capacity is good */  /* some drives have the word order reversed */  lba_sects = (lba_sects << 16) | (lba_sects >> 16);  if ((lba_sects - chs_sects) < _10_percent) {    lba_capacity = lba_sects;	/* fix it */    return true;		/* lba_capacity is (now) good */  }  return false;			/* lba_capacity value is bad */}/* * current_capacity() returns the capacity (in sectors) of a drive * according to its current geometry/LBA settings. */uint32_tide_drive::Capacity(){  struct ide_driveid *drvid = (ide_driveid*) id;#if 0  MsgLog::printf("l_chs: %d/%d/%d\n", l_chs.cyl, l_chs.hd, l_chs.sec);#endif    uint32_t capacity = l_chs.cyl * l_chs.hd * l_chs.sec;  if (!present)    return 0;    if (media != IDE::med_disk)    return UINT32_MAX;	/* cdrom or tape */  if (!IS_PROMISE_DRIVE) {    select.b.lba = 0;    /* Determine capacity, and use LBA if the drive properly supports it */    if (drvid != NULL && (drvid->capability & 2)	&& drvid->CheckLbaCapacity()) {      if (drvid->lba_capacity >= capacity) {	capacity = drvid->lba_capacity;	select.b.lba = 1;      }    }  }  return capacity;#if 0  return (capacity - drive->sect0);#endif}/* * This is used for most PIO data transfers *from* the IDE interface */voidide_drive::InputData(void *buffer, unsigned int wcount){  uint16_t io_base  = hwif->ioBase;  uint16_t data_reg = io_base+IDE_DATA;#if 0  uint8_t io_32bit = io_32bit;  if (io_32bit) {#if SUPPORT_VLB_SYNC    if (io_32bit & 2) {      cli();      do_vlb_sync(io_base+IDE_NSECS);      insw(data_reg, buffer, wcount);      if (drive->unmask)	sti();    } else#endif /* SUPPORT_VLB_SYNC */      insw(data_reg, buffer, wcount);  } else#endif    ins16(data_reg, buffer, wcount<<1);}/* * ide_multwrite() transfer sectors to the drive, being careful not to * overrun the drive's multCount limit. */voidide_drive::MultWrite (Request *req){  uint32_t nsec = req->req_nsec;  /* Can't do more than the block size limit: */  if (nsec > multCount)    nsec = multCount;  OutputData((void *) req->req_ioaddr, nsec * EROS_SECTOR_SIZE >> 2);  req->nsec = nsec;}/* * This is used for most PIO data transfers *from* the IDE interface */voidide_drive::OutputData(void *buffer, unsigned int wcount){  uint16_t io_base  = hwif->ioBase;  uint16_t data_reg = io_base+IDE_DATA;#if 0  uint8_t io_32bit = io_32bit;  if (io_32bit) {#if SUPPORT_VLB_SYNC    if (io_32bit & 2) {      cli();      do_vlb_sync(io_base+IDE_NSECS);      insw(data_reg, buffer, wcount);      if (drive->unmask)	sti();    } else#endif /* SUPPORT_VLB_SYNC */      insw(data_reg, buffer, wcount);  } else#endif    outs16(data_reg, buffer, wcount<<1);}voidide_fixstring (uint8_t *s, const int bytecount, const int byteswap){  uint8_t *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */  if (byteswap) {    /* convert from big-endian to host byte order */    for (p = end ; p != s;) {      unsigned short *pp = (unsigned short *) (p -= 2);      *pp = Machine::ntohhw(*pp);    }  }  /* strip leading blanks */  while (s != end && *s == ' ')    ++s;  /* compress internal blanks and strip trailing blanks */  while (s != end && *s) {    if (*s++ != ' ' || (s != end && *s && *s != ' '))      *p++ = *(s-1);  }  /* wipe out trailing garbage */  while (p != end)    *p++ = '\0';}voidide_drive::AtapiIdentify(uint8_t cmd){  int bswap;  /*  uint32_t capacity, check; */  id = (ide_driveid *) new (0) char[EROS_SECTOR_SIZE];    InputData(id, EROS_SECTOR_SIZE >> 2); /* read 512 bytes of id info */  MsgLog::printf("Got atapi data.\n");  /*   * EATA OPTION_SCSI controllers do a hardware ATA emulation:  ignore them   */  if ((id->model[0] == 'P' && id->model[1] == 'M')      || (id->model[0] == 'S' && id->model[1] == 'K')) {    MsgLog::printf("%s: EATA OPTION_SCSI HBA %40s\n", name, id->model);    present = 0;    return;  }  /*   *  WIN_IDENTIFY returns little-endian info,   *  WIN_PIDENTIFY *usually* returns little-endian info.   */  bswap = 1;  if (cmd == WIN_PIDENTIFY) {    if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */	|| (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */	|| (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */      bswap = 0;	/* Vertos drives may still be weird */  }  ide_fixstring (id->model,     sizeof(id->model),     bswap);  ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);  ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);  /*   * Check for an ATAPI device   */  if (cmd == WIN_PIDENTIFY) {    uint8_t type = (id->config >> 8) & 0x1f;    MsgLog::printf("%s: %s, ATAPI ", name, id->model);    switch (type) {    case 0:		/* Early cdrom models used zero */    case 5:      MsgLog::printf ("CDROM drive\n");      media = IDE::med_cdrom;      present = 0;		/* TEMPORARY */      removable = 1;      return;    case 1:#ifdef IDE_TAPE      MsgLog::printf ("TAPE drive");      if (idetape_identify_device (drive,id)) {	media = ide_tape;	present = 1;	removeable = 1;	if (hwif->dmaproc != NULL &&	    !hwif->dmaproc(ide_dma_check, drive))	  MsgLog::printf(", DMA");	MsgLog::printf("\n");      }      else {	present = 0;	MsgLog::printf ("\nide-tape: the tape is not supported by this version of the driver\n");      }      return;#else      MsgLog::printf ("TAPE ");      break;#endif /* CONFIG_BLK_DEV_IDETAPE */    default:      present = 0;      MsgLog::printf("Type %d - Unknown device\n", type);      return;    }    present = 0;    MsgLog::printf("- not supported by this kernel\n");    return;  }  /* check for removeable disks (eg. SYQUEST), ignore 'WD' drives */  if (id->config & (1<<7)) {	/* removeable disk ? */    if (id->model[0] != 'W' || id->model[1] != 'D')      removable = 1;  }  /* SunDisk drives: treat as non-removeable, force one unit */  if (id->model[0] == 'S' && id->model[1] == 'u') {    removable = 0;    if (select.b.unit) {      present = 0;      return;    }  }	  media = IDE::med_disk;  /* Extract geometry if we did not already have one for the drive */  if (!present) {    present = 1;    l_chs.cyl  = b_chs.cyl  = id->cyls;    l_chs.hd   = b_chs.hd   = id->heads;    l_chs.sec  = b_chs.sec  = id->sectors;  }  /* Handle logical geometry translation by the drive */  if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads      && (id->cur_heads <= 16) && id->cur_sectors)    {      /*       * Extract the physical drive geometry for our use.       * Note that we purposely do *not* update the bios info.       * This way, programs that use it (like fdisk) will       * still have the same logical view as the BIOS does,       * which keeps the partition table from being screwed.       *       * An exception to this is the cylinder count,       * which we reexamine later on to correct for 1024 limitations.       */      l_chs.cyl  = id->cur_cyls;      l_chs.hd   = id->cur_heads;      l_chs.sec  = id->cur_sectors;      /* check for word-swapped "capacity" field in id information */      uint32_t capacity = l_chs.cyl * l_chs.hd * l_chs.sec;      uint32_t check = (id->cur_capacity0 << 16) | id->cur_capacity1;      if (check == capacity) {	/* was it swapped? */	/* yes, bring it into little-endian order: */	id->cur_capacity0 = (capacity >>  0) & 0xffff;	id->cur_capacity1 = (capacity >> 16) & 0xffff;      }    }  /* Use physical geometry if what we have still makes no sense */  if ((!l_chs.hd || l_chs.hd > 16) && id->heads && id->heads <= 16) {    l_chs.cyl  = id->cyls;    l_chs.hd   = id->heads;    l_chs.sec  = id->sectors;  }  /* Correct the number of cyls if the bios value is too small */  if (l_chs.sec == b_chs.sec && l_chs.hd == b_chs.hd) {    if (l_chs.cyl > b_chs.cyl)      b_chs.cyl = l_chs.cyl;  }  (void) Capacity();  MsgLog::printf ("%s: %40s, %dMB w/%dkB Cache, %sCHS=%d/%d/%d",	  name, id->model, Capacity()/2048L,		  id->buf_size/2,	  select.b.lba ? "LBA, " : "",	  b_chs.cyl, b_chs.hd, b_chs.sec);  multCount = 0;  if (id->max_multsect) {    multReq = INITIAL_MULT_COUNT;    if (multReq > id->max_multsect)      multReq = id->max_multsect;    if (multReq || ((id->multsect_valid & 1) && id->multsect)) {      flags.b.setMultMode = 1;    }  }  if (hwif->dma_handler != 0) {	/* hwif supports DMA? */    if (!(hwif->dma_handler(ide_hwif::dma_check, this)))      MsgLog::printf(", DMA");  }  MsgLog::printf("\n");}uint8_tide_drive::Identify(uint8_t cmd){  uint32_t irqs = 0;  uint16_t hd_status = IDE_CTL_ALTSTATUS; /* non-intrusive preferred */  uint8_t rc = 1;    if (hwif->irq == 0) {    IRQ::EndProbe(IRQ::BeginProbe()); /* clear pending interrupts */    irqs = IRQ::BeginProbe();	/* wait for interrupt */    hwif->Put8(IDE_CTL_ALTSTATUS, ctl); /* enable device IRQ */  }  Machine::SpinWaitMs(DRIVE_DETECT_DELAY);    if ((hwif->Get8(IDE_CTL_ALTSTATUS) ^       hwif->Get8(IDE_STATUS)) &      ~INDEX_STAT) {    MsgLog::printf("%s: probing with STATUS instead of ALTSTATUS\n", name);    hd_status = IDE_STATUS;	/* use intrusive polling */  }  hwif->Put8(IDE_CMD, cmd);    uint64_t waitTill = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY);  waitTill = Machine::MillisecondsToTicks(waitTill);  waitTill += SysTimer::Now();    do {    if (SysTimer::Now() > waitTill) {      if (hwif->irq == 0)	(void) IRQ::EndProbe(irqs);      MsgLog::printf("Drive timed out.\n");      return 1;	/* drive timed-out */    }    Machine::SpinWaitMs(DRIVE_DETECT_DELAY);  } while (hwif->Get8(hd_status) & BUSY_STAT);  Machine::SpinWaitMs(DRIVE_DETECT_DELAY);	/* wait for IRQ and DRQ_STAT */  uint8_t status = hwif->Get8(IDE_STATUS);    if (OK_STAT(status,DRQ_STAT,BAD_R_STAT)) {    MsgLog::printf("Drive returned ID.  Fetching ATAPI info...\n");    /* Drive returned an ID.  Do an ATAPI identify: */    AtapiIdentify(cmd);#if 0    if (present && media != IDE::med_tape) {      /* tune the PIO mode... the BIOS ought to do this these days. */    }#endif    rc = 0;			/* drive responded with ID */  } else {    MsgLog::printf("Drive refused ID.\n");    rc = 2;			/* drive refused ID */  }  if (hwif->irq == 0) {    irqs = IRQ::EndProbe(irqs);	/* get irq number */    if (irqs > 0)      hwif->irq = irqs;    else				/* Mmmm.. multiple IRQs */      MsgLog::printf("%s: IRQ probe failed (%d)\n", name, irqs);  }  DEBUG(probe)    MsgLog::dprintf(true, "Pausing after drive probe...\n");  return rc;}voidide_hwif::Put8(uint16_t reg, uint8_t value){#if 0  MsgLog::printf("Writing byte to hwifno %d reg 0x%04x, %d\n",		 ndx, reg + ioBase, value);#endif  old_outb(reg + ioBase, value);

⌨️ 快捷键说明

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