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 + -
显示快捷键?