ide_drv.c

来自「ertfs文件系统里面既有完整ucos程序」· C语言 代码 · 共 1,289 行 · 第 1/3 页

C
1,289
字号


static BOOLEAN ide_command(byte command, PIDE_CONTROLLER pc, int logical_unit_number, dword blockno, word nblocks)
{
    ide_clear_voregs(pc);               /* Clear virtual output registers */
     /* Call ide_rdwr_setup to set transfer addresses, sector_count, sector_number,
        cylinder_low, cylinder_hi and drive_head */
    if ((command == IDE_CMD_ERASES) ||
        (command == IDE_CMD_READS) ||
        (command == IDE_CMD_READM) ||
        (command == IDE_CMD_WRITEM_NE) ||
        (command == IDE_CMD_WRITEM) ||
        (command == IDE_CMD_WRITES_NE) ||
        (command == IDE_CMD_WRITES) )
    {
        if (!ide_rdwr_setup(pc, logical_unit_number, blockno, nblocks))
            return(FALSE);
        pc->timer = (word)TIMEOUT_RDWR;
    }
    else
    {
        pc->timer = (word)TIMEOUT_TYPICAL;
    }
    pc->vo_drive_head  |= 0x20;                 /* Bit five is always one */
    if (logical_unit_number)                                /* Bit four is drive (1 or 0) */
        pc->vo_drive_head |= 0x10;              /* select device 1 (slave) */
    if (command == IDE_CMD_ERASES)              /* Erase sets bit 7 */
        pc->vo_drive_head |= 0x80;
    if (command == IDE_CMD_SETM)
    {
        pc->block_size = pc->drive[logical_unit_number].max_multiple;
        pc->vo_sector_count = pc->block_size;
    }
	/* 11-10-2000 - New code. Since we don't call SETM on each WRITEM or 
	   READM we must set block size now on the READM, WRITEM calls */
    if ((command == IDE_CMD_READM)||
    	(command == IDE_CMD_WRITEM))
    {
        pc->block_size = pc->drive[logical_unit_number].max_multiple;
    }

	pc->vo_command = command;

    /* Call the processing routine */
    return(ide_do_command(pc));
}

/* ........................................................................
    .... End Device driver external interface routines.             .......
    .......................................................................*/
#if (IDE_USE_SET_FEATURES)
static BOOLEAN ide_set_features(PIDE_CONTROLLER pc, int logical_unit_number, byte feature, byte config) /* __fn__ */
{
    ide_clear_voregs(pc);               /* Clear virtual output registers */
    if (logical_unit_number)                            /* Bit four is drive (1 or 0) */
        pc->vo_drive_head |= 0x10;          /* select device 1 (slave) */
    pc->vo_feature      = feature;
    pc->vo_sector_count = config;
    pc->vo_command = IDE_CMD_SETF;
    pc->timer = (word)TIMEOUT_TYPICAL;
 /* Call the processing routine */
    return(ide_do_command(pc));
}
#endif

//DM: commented out conditional to re-enable. Put back conditional if
//    routine really is no good for CDROM
//#ifndef CDFS_ONLY

/* Command execution routines. */

/*  ide_command_diags - Execute drive diagnostics
*
*
*
* Returns:
*   TRUE if the drive diagnostic succeeded else FALSE.
*   
*   This routine execute a diagnostic request. If the diagnostic succeeds
*   it returns TRUE otherwise it returns FALSE.
*
* called by: ide_drive_init
*
* This routine is portable
*   
*/

static BOOLEAN ide_command_diags(PIDE_CONTROLLER pc) /* __fn__ */
{
    ide_clear_voregs(pc);               /* Clear virtual output registers */
    pc->vo_command = IDE_CMD_DIAG;      /* Command */
    pc->timer = (word)TIMEOUT_DIAG;
    return(ide_do_command(pc));
}
//DM: commented out conditional to re-enable
//#endif // CDFS_ONLY



/*  ide_command_setparms - Set drive parameters
*
* Inputs:
*   drive       - Drive number (0 or 1)
*   heads      - 
*   sectors
*
* Returns:
*   TRUE on success else FALSE. If FALSE pc->error_no will contain the error.
*   
*   This routine tells the drive how many heads and sectors per track
*   we will be basing our block to sector:trak:head calculations. The
*   drive will map sector:trak:head to its internal geometry. 
*
* This routine is portable
*/

#if (USE_SETPARMS)

static BOOLEAN ide_command_setparms(PIDE_CONTROLLER pc, int logical_unit_number, byte heads, byte sectors) /* __fn__ */
{
byte max_head;

    ide_clear_voregs(pc);                 /* Clear virtual output registers */
  /* Sectors per track go in sector count register */
    pc->vo_sector_count  = sectors;
  /* number of heads - 1 goes in the drive head register */
    max_head = (byte) (heads - 1);
    pc->vo_drive_head    = (byte) (max_head & 0x0f);   /* bit 0-3 is head */
    pc->vo_drive_head    |= 0x20;                   /* Bit five is always one */
    if (logical_unit_number)                                     /* Bit four is drive (1 or 0) */
        pc->vo_drive_head |= 0x10;
    pc->vo_command = IDE_CMD_INITP;
    pc->timer = (word)TIMEOUT_TYPICAL;
 /* Call the processing routine */
    return(ide_do_command(pc));
}

#endif


/*  ide_command_read_multiple - Execute read multiple commands
*
* Inputs:
*   address - Destination address for the data
*   drive       - Drive number (0 or 1)
*   blockno - Block number to read
*   nblocks - Number of blocks (legal range: 1-256)
*
* Returns:
*   TRUE on success else FALSE. If FALSE pc->error_no will contain the error.
*   
*   This is the read worker function for the device driver. It performs a one
*   or many block read operation. It calls ide_rdwr_setup() and ide_do_command
*   to do most of the work.
*
*   Called by ide_io
*
* This routine is portable
*/

static BOOLEAN ide_command_read_multiple(PIDE_CONTROLLER pc, int logical_unit_number, dword blockno, word nblocks) /* __fn__ */
{
    /* Call set multiple */
/* 11-10=2000 - Don't call this, we did it in the open
    if (!ide_command(IDE_CMD_SETM, pc, logical_unit_number, 0, 0))
        return(FALSE);
*/
 
    return(ide_command(IDE_CMD_READM, pc, logical_unit_number, blockno, nblocks));
}

/*  ide_command_write_multiple - Execute write_multiple(s) command
*
* Inputs:
*   address - Source address for the data
*   drive       - Drive number (0 or 1)
*   blockno - Block number to read
*   nblocks - Number of blocks (legal range: 1-256)
*
* Returns:
*   TRUE on success else FALSE. If FALSE pc->error_no will contain the error.
*   
*   This routine performs a one or many block write operation. It calls
*   ide_rdwr_setup() and ide_do_command() to do most of the work.
*   
*   This is the write worker function for the device driver. It performs a one
*   or many block write operation. It calls ide_rdwr_setup() and ide_do_command
*   to do most of the work.
*
*   Called by ide_do_block
*
* This routine is portable
*/

static BOOLEAN ide_command_write_multiple(PIDE_CONTROLLER pc, int logical_unit_number, dword blockno, word nblocks) /* __fn__ */
{
    /* Call set multiple */
/* 11-10=2000 - Don't call this, we did it in the open
    if (!ide_command(IDE_CMD_SETM, pc, logical_unit_number, 0, 0))
        return(FALSE);
*/
    return(ide_command(IDE_CMD_WRITEM, pc, logical_unit_number, blockno, nblocks));
}

/* ide_rdwr_setup - Setup routine for read  for read and write operations
* Inputs:
*   address - Destination address for the data
*   drive       - Drive number (0 or 1)
*   blockno - Block number to read/write
*   nblocks - Number of blocks (legal range: 1-256)
*
* Outputs:
*   
*   Sets the following register file registers:
*   vo_sector_count,vo_sector_number,vo_cyl_low,vo_cyl_high,vo_drive_head
*
*   
* Returns:
*   TRUE if inputs are valid else FALSE
*   
*   This routine performs setup operations common to read and write. The
*   registers listed above are set up and the user transfer address is
*   set up.
*
* This routine is portable
*
*/

static  byte l_to_byte(dword l, int bit)
{
    if (bit)
        l >>= bit;
    l &= 0xff;
    return( (byte) l);
}

static BOOLEAN ide_rdwr_setup(PIDE_CONTROLLER pc, int logical_unit_number, dword blockno, word nblocks) /* __fn__ */
{
word cylinder;
word head;
word sector;
word blk_offs_in_cyl;

    /* Check against max_blocks. this is 256 for IDE. In real mode 80xx we limit
        it to 128 to eliminate segment wrap. */
    if ( (nblocks > MAX_BLOCKS) || 
        (logical_unit_number && logical_unit_number != 1)  ||               /* Can only be 0 or 1 */
        (!pc->drive[logical_unit_number].sec_p_cyl&&(pc->drive[logical_unit_number].media_descriptor!=0xef)) )  /* Will be non zeroe if the */ /* CDROM Jerry*/
    {                                               /* Drive is initted */
        pc->error_code      = IDE_ERC_ARGS;
        return(FALSE);                          /* Drive is initted */
    }
        
    if (pc->drive[logical_unit_number].supports_lba)
    {
        pc->vo_sector_number = (byte) l_to_byte(blockno, 0);
        pc->vo_cyl_low      = (byte) l_to_byte(blockno, 8);
        pc->vo_cyl_high     = (byte) l_to_byte(blockno, 16);
        pc->vo_drive_head   = (byte) (l_to_byte(blockno, 24) & 0x0f);  
        pc->vo_drive_head   |= 0x40;  /* Select lba mode */
    }
    else
    {
    /* Calculate block address */
        cylinder = (word) (blockno/pc->drive[logical_unit_number].sec_p_cyl);
        blk_offs_in_cyl = (word) (blockno%pc->drive[logical_unit_number].sec_p_cyl);
        head = (word) (blk_offs_in_cyl/pc->drive[logical_unit_number].sec_p_track);
        sector = (word) (blk_offs_in_cyl%pc->drive[logical_unit_number].sec_p_track);
/* Load registers */
        pc->vo_sector_number = (byte) (sector + 1); /* Note in 1-N order at the controller */
        pc->vo_cyl_low      = (byte) (cylinder & 0xff);
        pc->vo_cyl_high     = (byte) (cylinder >> 8);
        pc->vo_drive_head   = (byte) (head & 0x0f); /* bit 0-3 is head */
    }
    pc->vo_sector_count  = (byte) nblocks; /* 1-255. 256 becomes 0 */
    return(TRUE);
}


static  BOOLEAN ide_error(PIDE_CONTROLLER pc, word error_code) 
{
    pc->error_code = error_code;
    pc->command_complete = TRUE;            /* Shesssaaa hung */
    return(FALSE);
}

/*  ide_do_command- Execute an ide cmd and poll or block until complete
* Inputs:
*   uses values in cs (the control structure)
* Outputs:
*   pc->command_complete and pc->error_code are updated. As well as all the 
*   vi_xxx (virtual register file in) are read in.
* Returns:
*   TRUE if command succeeded else look in error_code.
*   
*   This routine is called by all ide_command_xxx routines to begin 
*   command executiion. It copies the vo_xxxxx register file from the control
*   structure to the drive and then polls or blocks until completion. If 
*   pc->vo_dig_out & IDE_OUT_IEN is true it polls waiting for completion,
*   otherwise it blocks waiting for the ISR to wake it. Each command has
*   a time out value in pc->timer.
*
*/

static BOOLEAN ide_do_command(PIDE_CONTROLLER pc) /* __fn__ */
{
    word        utemp;
    int         loop_count=8;

    pc->error_code = 0; 
    pc->command_complete = FALSE;
    
 /* The drive may be busy. If so, wait for it to complete */

    if (!ide_wait_not_busy(pc, pc->timer))
        return(ide_error(pc,IDE_ERC_BUS));                  /* bus is hung */
 
    /* There should be no pending interrupt complete signals
        so call os_ide_clear_signal(); to be sure we aren't 
        signalled until after an int occurs. */
        
    if (!(pc->vo_dig_out & IDE_OUT_IEN))
    {
        OS_IDE_SIGNAL_BIND(pc->controller_number);
        OS_IDE_SIGNAL_CLEAR(pc->controller_number);
    }

 /* Now load the rest of the register file. (send command last) */
    ide_wr_sector_count(pc, pc->vo_sector_count);
    ide_wr_sector_number(pc, pc->vo_sector_number);
    ide_wr_cyl_low(pc,  pc->vo_cyl_low);
    ide_wr_cyl_high(pc, pc->vo_cyl_high);
    ide_wr_drive_head(pc, pc->vo_drive_head);
    ide_wr_feature(pc, pc->vo_feature);
 /* Write the digital output register (interrupts on/off) */
    ide_wr_dig_out(pc, pc->vo_dig_out);
 /* The register file is loaded.Now load the command into the command register. */
      
    ide_wr_command(pc, pc->vo_command);
    
    if ((pc->vo_command == IDE_CMD_WRITES) ||
        (pc->vo_command == IDE_CMD_WRITES_NE) ||
        (pc->vo_command == IDE_CMD_WRITEM) ||
        (pc->vo_command == IDE_CMD_WRITEM_NE) )
    {
        /* Busy should deassert. DRQ should assert */
        if (!(ide_wait_not_busy(pc, pc->timer) && ide_wait_drq(pc, pc->timer)))
        {
            pc->error_code = IDE_ERC_BUS;
            pc->command_complete = TRUE;            /* Shesssaaa hung */
            return(FALSE);
        }
        if (pc->sectors_remaining >= (word) pc->block_size)
            utemp = (word) pc->block_size;
        else
            utemp = pc->sectors_remaining;
    
        ide_out_words(pc, (word) (utemp << 8));/* 256 times xfer size */
        pc->sectors_remaining -= utemp;
    }

 /* The NEW SMART WAY OF DOING IT. DOING THE DATA TRANSFER AT THE APP LAYER */
    while (!pc->command_complete)
    {
        if (!(pc->vo_dig_out & IDE_OUT_IEN))    /* Interupt mode */
        {
            /* Wait for a signal of completion from ide_interrupt. Pass in 
               pc->timer (# ticks to wait before detecting a timeout */
            if (!OS_IDE_SIGNAL_TEST(pc->controller_number, pc->timer))
                goto bail;
        }
        else
        {
            io_delay(); /* Give time for BUSY to assert */
            io_delay();
            if (!ide_wait_not_busy(pc, pc->timer)) /* Polled mode */
            {
    bail:
                pc->error_code      = IDE_ERC_TIMEOUT;
                pc->command_complete = TRUE;            /* Shesssaaa hung */
            }
    	}
    	if (!pc->command_complete)
	    	ide_interrupt(pc->controller_number);
    }
    
    if (pc->error_code) 
        return(FALSE);
    else
        return(TRUE);
}

/*  ide_interrupt - Respond to the ide_disk interupt.
* Inputs:
*   uses values in cs (the control structure)
* Outputs:
*   pc->command_complete and pc->error_code are set. If error_code is non zero
*   the command failed.
* Returns:
*   Nothing
*   
*   This routine is called by ide_isr or do_command if it is polling. Its job
*   is to
*   determine the cause of the IDE disk interrupt and process it correctly.
*   On multi block reads and writes it performs the data transfer and returns if
*   the multiblock transfer is incomplete. In other cases it checks the status 
*   bits for errors. If no errors were found pc->error_code is zero other wise
*   pc->error_code is set to an appropriate value. If the driver is in interrupt
*   mode the task is woken up when command_complete is set.
*
*/

void ide_interrupt(int controller_number)                       /* __fn__ */
{
word utemp;
PIDE_CONTROLLER pc;

    pc = &controller_s[controller_number];

 /* Read the status register. This also clears the interrupt status if pending */
    pc->vi_status = ide_rd_status(pc);

 /* Read the sector count register. We'll need it in reads and writes */
    pc->vi_sector_count = ide_rd_sector_count(pc);

  /* Check if the drive reported an error */
    if (pc->vi_status & IDE_STB_ERROR)
    {
// BUGBUGBUG - For READS and READM commands we need to read the data 
// to put the controller in command mode

⌨️ 快捷键说明

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