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

📄 bddrv.c

📁 linux1.0的源码
💻 C
📖 第 1 页 / 共 5 页
字号:
}
/*****************************************************************************
*****************************************************************************/
static int ide_select_drive(unsigned short ioadr, unsigned char unit)
{

    DEBUG(printf("ide_select_drive: ioadr=0x%X, unit=0x%X\n",
                 ioadr, unit);)
    outportb(ioadr + ATA_REG_DRVHD, unit);
    /* Hale Landis: ATA-4 needs delay after drive select/head reg written */
    nsleep(400);
    /* wait up to 5 seconds (longer for laptop or APM computers) for status =
    BUSY=0  READY=1  DF=?  DSC=?		DRQ=0  CORR=?  IDX=?  ERR=? */
    //return ide_poll_status(ioadr, 5000, 0xC8, 0x40);

    /* "ATA Packet Commands can be issued regardless of the state of the
    DRDY Status Bit."  "1. The host Polls for BSY=0, DRQ=0 then initializes
    the task file by writing the required parameters to the Features,
    Byte Count, and Drive/Head registers."

    BUSY=0  READY=?  DF=?  DSC=?		DRQ=0  CORR=?  IDX=?  ERR=? */
    return ide_poll_status(ioadr, 5000, 0x88, 0x00);
}
/*****************************************************************************
### - report time spent waiting for interrupt, like ide_poll_status() does
*****************************************************************************/
static int ide_await_interrupt(unsigned short ioadr,
                               unsigned short irqmask, unsigned timeout,
                               unsigned char stat_mask, unsigned char stat_bits)
{
    int temp;
    unsigned char stat;

    DEBUG(printf("ide_await_interrupt: ioadr=0x%X, irqmask=0x%X, "
                 "timeout=%u, stat mask=0x%X, stat bits=0x%X\n",
                 ioadr, irqmask, timeout, stat_mask, stat_bits);)
    temp = await_interrupt(irqmask, timeout);
    /* timeout */
    if(temp == 0)
    {
        printf("ide_await_interrupt: error: timeout\n");
        return -1;
    }
    /* no timeout, but did we get the error status we wanted? */
    stat = inportb(ioadr + ATA_REG_STATUS);
    if((stat & stat_mask) != stat_bits)
    {
        printf("ide_await_interrupt: error: bad status 0x%X\n",
               stat);
        return -1;
    }
    return 0;
}
/*****************************************************************************
*****************************************************************************/
#define min(a,b)    (((a) < (b)) ? (a) : (b))

static void ide_write_regs(ide_t *dev, unsigned long blk, unsigned num_blks)
{
    unsigned num_sects;
    unsigned short ioadr, sector, head;

    DEBUG(printf("ide_write_regs:\n");)
    ioadr = dev->blkdev.io.adr[0];
    num_sects = min(num_blks, dev->mult_count);
    outportb(ioadr + ATA_REG_COUNT, num_sects);	/* num sectors */
    /* add partition start */
    //	DEBUG(printf("ide_write_regs: blk=%lu relative; ", blk);)
    //	blk += dev->start_blk;
    DEBUG(printf("blk=%lu absolute, num_sects=%u of %u, ",
                 blk, num_sects, num_blks);)
    /* LBA */
    if(dev->has_lba && dev->use_lba)
    {
        DEBUG(printf("LBA\n");)
        outportb(ioadr + ATA_REG_SECTOR, blk);	/* sector */
        blk >>= 8;
        outportb(ioadr + ATA_REG_LOCYL, blk);	/* cylinder LSB */
        blk >>= 8;
        outportb(ioadr + ATA_REG_HICYL, blk);	/* cylinder MSB */
        blk >>= 8;
        blk &= 0x0F;
        outportb(ioadr + ATA_REG_DRVHD, 0x40 |	/* head */
                 dev->blkdev.unit | blk);	/* b6 enables LBA */
    }
    /* CHS */
    else
    {
        /* I thought maybe the ANSI div() or ldiv() functions
        could save us two divides, but those functions are
        suprisingly large */
        sector = blk % dev->blkdev.sectors + 1;
        blk /= dev->blkdev.sectors;
        head = blk % dev->blkdev.heads;
        blk /= dev->blkdev.heads;

        DEBUG(printf("CHS=%lu:%u:%u\n", blk, head, sector);)
        outportb(ioadr + ATA_REG_SECTOR, sector);/* sector */
        outportb(ioadr + ATA_REG_LOCYL, blk);	/* cylinder LSB */
        blk >>= 8;
        outportb(ioadr + ATA_REG_HICYL, blk);	/* cylinder MSB */
        outportb(ioadr + ATA_REG_DRVHD,		/* head */
                 dev->blkdev.unit | head);
    }
    /* Hale Landis: ATA-4 needs delay after drive select/head reg written */
    nsleep(400);
}
/*****************************************************************************
xxx - finish
*****************************************************************************/
#pragma argsused
static int ide_error(request_t *req)
{
    DEBUG(printf("*** ide_error: ***\n");)

    (req->errors)++;
    if(req->errors >= 3)
        return -1;
    return 0;
}
/*****************************************************************************
xxx - this should be able to disable multmode, as well
*****************************************************************************/
static int ide_enable_multmode(ide_t *dev)
{
    unsigned short ioadr;
    int err;

    DEBUG(printf("ide_enable_multmode:\n");)
    ioadr = dev->blkdev.io.adr[0];
    for(;;)
    {
        /* select drive */
        err = ide_select_drive(ioadr, dev->blkdev.unit);
        if(err == 0)
        {
            /* issue SET MULTIPLE MODE command */
            outportb(ioadr + ATA_REG_COUNT, dev->mult_count);
            outportb(ioadr + ATA_REG_CMD, ATA_CMD_MULTMODE);
            nsleep(400);
            /* wait up to 10 seconds for status =
            BUSY=0  READY=1  DF=?  DSC=?		DRQ=0  CORR=?  IDX=?  ERR=0 */
            err = ide_await_interrupt(ioadr,
                                      dev->blkdev.io.irq, 10000, 0xC9, 0x40);
            if(err == 0)
            {
                /* it worked: enable multi-sector reads */
                dev->use_multmode = 1;
                break;
            }
        }
        /* error, retry */
        //		err = ide_error(dev);xxx - needs req_t, not ide_t
        if(err != 0)
            break;
    }
    return err;
}
/*****************************************************************************
*****************************************************************************/
int ide_read_sectors(request_t *req)
{
    ide_t *dev;
    unsigned short ioadr;
    int err;

    DEBUG(printf("ide_read_sectors:\n");)
    dev = (ide_t *)req->dev;
    /* if the ide_t struct had a magic value in it, we could validate it here */
    ioadr = dev->blkdev.io.adr[0];
    /* select drive (xxx - need to do this?) */
    err = ide_select_drive(ioadr, dev->blkdev.unit);
    if(err != 0)	/* xxx - no retry? */
        return err;
    /* write drive registers, except for command byte */
AGAIN:
    ide_write_regs(dev, req->blk, req->num_blks);
    /* write command byte. Use multi-sector reads if the drive supports it */
    outportb(ioadr + ATA_REG_CMD,
             (dev->has_multmode && dev->use_multmode) ?
             ATA_CMD_READMULT : ATA_CMD_READ);
    nsleep(400);
    /* main read loop */
    while(req->num_blks != 0)
    {
        unsigned short num_blks, num_bytes;

        /* wait up to 10 seconds for status =
        BUSY=0  READY=?  DF=?  DSC=?		DRQ=1  CORR=?  IDX=?  ERR=0 */
        err = ide_await_interrupt(ioadr, dev->blkdev.io.irq,
                                  10000, 0x89, 0x08);
        /* call ide_error() if there is an error */
        if(err != 0)
        {
            err = ide_error(req);
            /* if ide_error() returns non-zero, then we are doomed... */
            if(err != 0)
                return err;
            /* ...else try again */
            else
                goto AGAIN;
        }
        /* transfer as many sectors as multiple mode allows */
        num_blks = min(req->num_blks, dev->mult_count);
        num_bytes = num_blks * dev->blkdev.bytes_per_blk;
        /* read! */
        insw(ioadr + ATA_REG_DATA, (unsigned short *)req->buf,
             num_bytes >> 1);
        DEBUG(printf("ide_read_sectors: read %u sectors of %u\n",
                     num_blks, req->num_blks);)
        /* advance pointers */
        req->num_blks -= num_blks;
        req->blk += num_blks;
        req->buf += num_bytes;
    }
    return 0;
}
/*****************************************************************************
xxx - finish
*****************************************************************************/
#pragma argsused
static int ide_write_sectors(request_t *req)
{
    return -1;
}
/*****************************************************************************
*****************************************************************************/
static unsigned short atapi_read_and_discard(unsigned short ioadr,
        unsigned char *buf, unsigned short want)
{
    unsigned short got, count;

    got = inportb(ioadr + ATAPI_REG_HICNT);
    got <<= 8;
    got |= inportb(ioadr + ATAPI_REG_LOCNT);
    DEBUG(printf("atapi_read_and_discard: want %u bytes, got %u\n",
                 want, got);)
    count = min(want, got);
    insw(ioadr + ATA_REG_DATA, (unsigned short *)buf, count >> 1);
    /* excess data? read and discard it */
    if(got > count)
    {
        /* read only 16-bit words where possible */
        for(count = got - count; count > 1; count -= 2)
            (void)inport(ioadr + ATA_REG_DATA);
        /* if the byte count is odd, read the odd byte last */
        if(count != 0)
            (void)inportb(ioadr + ATA_REG_DATA);
    }
    /* return number of bytes read into memory (not discarded) */
    return count;
}
/*****************************************************************************
*****************************************************************************/
static int ide_identify(ide_t *idedev)
{
    unsigned char temp1, temp2, id_cmd, buf[512], swap_chars;
    unsigned short ioadr, temp, id_delay;
    blkdev_t *blkdev;

    DEBUG(printf("ata_identify:\n");)
    blkdev = &idedev->blkdev;
    ioadr = blkdev->io.adr[0];
    /* sector count and sector registers both set to 1 after soft reset */
    temp1 = inportb(ioadr + ATA_REG_COUNT);
    temp2 = inportb(ioadr + ATA_REG_SECTOR);
    if(temp1 != 0x01 || temp2 != 0x01)
    {
        printf("no drive on interface 0x%X\n", ioadr);
        return -1;
    }
    /* check cylinder registers */
    temp1 = inportb(ioadr + ATA_REG_LOCYL);
    temp2 = inportb(ioadr + ATA_REG_HICYL);
    temp = inportb(ioadr + ATA_REG_STATUS);
    /* ATAPI device puts 0xEB14 in the cylinder registers */
    if(temp1 == 0x14 && temp2 == 0xEB)
    {
        (void)atapi_read_and_discard(ioadr, NULL, 0);
        /* now then, where were we? */
        printf("ATAPI drive, ");
        id_cmd = ATA_CMD_PID;
        id_delay = 10000;	/* WAIT_PID */
        blkdev->bytes_per_blk = 2048;
    }
    /* ATAPI device puts 0 in the cylinder registers */
    else if(temp1 == 0 && temp2 == 0 && temp != 0)
    {
        printf("ATA drive, ");
        id_cmd = ATA_CMD_ID;
        id_delay = 30000;	/* WAIT_ID */
        blkdev->bytes_per_blk = 512;
    }
    else
    {	printf("unknown drive type\n");
        return -1;
    }
    _interrupt_occurred = 0;
    /* issue ATA or ATAPI IDENTIFY DEVICE command, then get results */
    outportb(ioadr + ATA_REG_CMD, id_cmd);
    nsleep(400);
    if(await_interrupt(0xC000, id_delay) == 0)
    {
        /* could be very old drive that doesn't support IDENTIFY DEVICE.
        Read geometry from partition table? Use CMOS? */
        printf("ata_identify: IDENTIFY DEVICE failed\n");
        return -1;
    }
    insw(ioadr + ATA_REG_DATA, (unsigned short *)buf, 256);/* xxx - read ATAPI xfer count */
    /* print some info */
    swap_chars = 1;
    /* model name is not byte swapped for NEC, Mitsumi FX, and Pioneer CD-ROMs */
    if(id_cmd == ATA_CMD_PID)
    {
        if((buf[54] == 'N' && buf[55] == 'E') ||
                (buf[54] == 'F' && buf[55] == 'X') ||
                (buf[54] == 'P' && buf[55] == 'i'))
            swap_chars = 0;
    }
    for(temp = 54; temp < 94; temp += 2)
    {
        putchar(buf[(temp + 0) ^ swap_chars]);
        putchar(buf[(temp + 1) ^ swap_chars]);
    }
    blkdev->cyls = read_le16(buf + 2);
    blkdev->heads = read_le16(buf + 6);
    blkdev->sectors = read_le16(buf + 12);
    printf("\nCHS=%u:%u:%u, ", blkdev->cyls, blkdev->heads,
           blkdev->sectors);
    if((buf[99] & 1) != 0)
    {
        printf("DMA, ");
        idedev->has_dma = 1;
    }
    if((buf[99] & 2) != 0)
    {
        printf("LBA, ");
        idedev->has_lba = 1;
    }
    if(((buf[119] & 1) != 0) && (buf[118] != 0))
    {
        temp = buf[94];
        printf("mult_count=%u, ", temp);
    }
    else
        temp = 1;
    idedev->mult_count = temp;
    printf("%uK cache\n", read_le16(buf + 42) >> 1);
#if 0
    /* PIO and DMA transfer modes indicate how fast the drive can move data.
    This is of interest only if you have an intelligent IDE controller
    (e.g. EIDE VLB chip for 486 system, or Triton PCI for Pentium), where the
    controller can be programmed to move data at the fastest rate possible

    xxx - these may not be correct */
    printf("max PIO mode %u\n", read_le16(buf + 102));
    printf("max DMA mode %u\n", read_le16(buf + 104));
    printf("single-word DMA status 0x%X\n", read_le16(buf + 124));
    printf("multi-word DMA status 0x%X\n", read_le16(buf + 126));
    printf("advanced PIO mode supported=%u\n", buf[128]);
    /* bits b6:b5 indicate if the drive can generate an IRQ when DRQ goes high.
    This is good for performance, since you don't have to sit and poll the
    drive for 3 milliseconds. (This code always polls for DRQ.) */
    switch((buf[0] >> 5) & 3)
    {
    case 0:
        printf("  polled DRQ\n");
        break;
    case 1:
        printf("  interrupt DRQ\n");

⌨️ 快捷键说明

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