ide_drv.c
来自「ertfs文件系统里面既有完整ucos程序」· C语言 代码 · 共 1,289 行 · 第 1/3 页
C
1,289 行
/*
* Portable IDE Device driver.
*
*/
// 10-24-2000 added LBA formatting. Submit to main tree
#include "pcdisk.h"
#define IDE_USE_SET_FEATURES 0
/* if one use the following set features call when the drive is opened:
IDE_FEATURE_SETPERF Set Perfomance Option
IDE_FEATURE_WCACHE_ON Enable the write cache
IDE_FEATURE_DEFECT_ON Enable the defect re-assignment
IDE_FEATURE_RLOOK_ON Enable read look-ahead
*/
#define OS_IDE_SIGNAL_TEST(X,Y) ks_test_ide_signal(X, Y)
#define OS_IDE_SIGNAL_CLEAR(X) ks_clear_ide_signal(X)
#define OS_IDE_SIGNAL_BIND(X)
void ks_invoke_ide_interrupt(int controller_number);
void hook_ide_interrupt(int irq, int controller_number);
void ks_clear_ide_signal(int controller);
BOOLEAN ks_test_ide_signal(int controller, word timeout);
#define ZERO 0
/* Set USE_SETPARMS to 1 to force this driver to set the drive to use its
true geometry (cyls, hds, sec/track) when this driver initializes.
This is only necessary if the IDE drive had already been set to use
a translated geometry during BIOS initialization at boot. It may also
be needed for ancient IDE drives. It should not cause any harm on
newer devices -- it just adds a little unnecessary code. */
#define USE_SETPARMS 0
#if (USE_ATA)
#if (IDE_USE_SET_FEATURES)
static BOOLEAN ide_set_features(PIDE_CONTROLLER pc, int phys_drive, byte feature, byte config);
#endif
static BOOLEAN ide_command(byte command, PIDE_CONTROLLER pc, int phys_drive, dword blockno, word nblocks);
BOOLEAN atapi_command(byte command, PIDE_CONTROLLER pc, int phys_drive);
/*static BOOLEAN ide_reset(PIDE_CONTROLLER pc);*/
static BOOLEAN ide_command_diags(PIDE_CONTROLLER pc);
static BOOLEAN ide_command_read_multiple(PIDE_CONTROLLER pc, int phys_drive, dword blockno, word nblocks);
static BOOLEAN ide_command_write_multiple(PIDE_CONTROLLER pc, int phys_drive, dword blockno, word nblocks);
static BOOLEAN ide_rdwr_setup(PIDE_CONTROLLER pc, int phys_drive, dword blockno, word nblocks);
static BOOLEAN ide_do_command(PIDE_CONTROLLER pc);
#if (USE_SETPARMS)
static BOOLEAN ide_command_setparms(PIDE_CONTROLLER pc, int phys_drive, byte heads, byte sectors);
#endif
static void ide_clear_voregs(PIDE_CONTROLLER pc);
static void ide_read_register_file(PIDE_CONTROLLER pc);
BOOLEAN ide_in_words(PIDE_CONTROLLER pc, word nwords);
BOOLEAN ide_out_words(PIDE_CONTROLLER pc, word nwords);
extern IDE_CONTROLLER controller_s[N_ATA_CONTROLLERS];
#ifndef ide_rd_status
/* If ide_rd_status is defined it is a macro in ide_drv.h */
#define ide_inbyte(X) ((byte) INBYTE((X)))
#define ide_outbyte(X,Y) OUTBYTE((X), (byte)(Y))
byte ide_rd_status(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_STATUS));
}
byte ide_rd_sector_count(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_SECTOR_int));
}
byte ide_rd_alt_status(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_ALT_STATUS));
}
byte ide_rd_error(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_ERROR));
}
byte ide_rd_sector_number(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_SECTOR_NUMBER));
}
byte ide_rd_cyl_low(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_CYL_LOW));
}
byte ide_rd_cyl_high(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_CYL_HIGH));
}
byte ide_rd_drive_head(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_DRIVE_HEAD));
}
byte ide_rd_drive_address(PIDE_CONTROLLER pc) /* __fn__ */
{
return(ide_inbyte(pc->register_file_address + IDE_OFF_DRIVE_ADDRESS));
}
void ide_wr_dig_out(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_DIG_OUTPUT, v);
}
void ide_wr_sector_count(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_SECTOR_int, v);
}
void ide_wr_sector_number(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_SECTOR_NUMBER, v);
}
void ide_wr_cyl_low(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_CYL_LOW, v);
}
void ide_wr_cyl_high(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_CYL_HIGH, v);
}
void ide_wr_drive_head(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_DRIVE_HEAD, v);
}
void ide_wr_command(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_COMMAND, v);
}
void ide_wr_feature(PIDE_CONTROLLER pc, byte v) /* __fn__ */
{
ide_outbyte(pc->register_file_address + IDE_OFF_FEATURE, v);
}
#endif /* #ifndef ide_rd_status */
/* void ide_in_words(word nwords)
*
* This routine reads nwords 16 bit words from the IDE data register
* and places it in the buffer at user_address. It must increment the
* user_address pointer so future calls will work correctly. In the IBM
* PC port we do an equivalent operation in assembler and update
* user_offset and user_segment.
*
* This has to be FAST !!!!
*
*/
BOOLEAN ide_in_words(PIDE_CONTROLLER pc, word nwords) /* __fn__ */
{
unsigned short KS_FAR *p;
pc->vi_status = ide_rd_status(pc);
if (!(pc->vi_status & IDE_STB_DRQ)) /* Drive should have data ready */
{
/* Serious problem. Bus state incorrect */
pc->error_code = IDE_ERC_BUS;
return(FALSE);
}
/* Note: the word wide data port is at offset 0 in the register file */
p = (unsigned short KS_FAR *) pc->user_address;
insw(pc->register_file_address + IDE_OFF_DATA, p, nwords);
pc->user_address += (nwords*2);
return(TRUE);
}
/* extern void ide_out_words(word nwords)
*
* This routine writes nwords 16 bit words from the buffer at
* user_address to the IDE data register.
* It must increment the user_address pointer so future calls will work
* correctly. In the IBM PC port we do an equivalent operation in assembler
* and update user_offset and user_segment.
*
* This is just a sample. This has to be FAST !!!!
*
*/
BOOLEAN ide_out_words(PIDE_CONTROLLER pc, word nwords) /* __fn__ */
{
unsigned short KS_FAR *p;
pc->vi_status = ide_rd_status(pc);
if (!(pc->vi_status & IDE_STB_DRQ)) /* Drive should be requesting */
{
pc->error_code = IDE_ERC_BUS;
return(FALSE);
}
p = (unsigned short KS_FAR *) pc->user_address;
outsw((IOADDRESS)(pc->register_file_address + IDE_OFF_DATA), p, nwords);
pc->user_address += (nwords*2);
return(TRUE);
}
/* ide_isr() - IDE ISR routine.
*
* This routine is a portable interrupt service routine.
*
*/
void KS_FAR ide_isr(int controller_number) /*__fn__*/
{
PIDE_CONTROLLER pc;
pc = &controller_s[controller_number];
/* Read the status register. to clear the interrupt status */
pc->vi_status = ide_rd_status(pc);
/* Signal that we got an interrupt */
ks_invoke_ide_interrupt(controller_number);
}
/* Open function. This is pointed to by the bdevsw[] table */
/*
* Note: This routine is called with the drive already locked so
* in several cases there is no need for critical section code handling
*/
void ide_eject_media(DDRIVE *pdr)
{
PIDE_CONTROLLER pc;
pc = &controller_s[pdr->controller_number];
}
BOOLEAN ide_drive_open(DDRIVE *pdr) /*__fn__*/
{
byte heads;
byte sectors;
dword ltemp;
dword ltemp2;
word buf[256];
PIDE_CONTROLLER pc;
pc = &controller_s[pdr->controller_number];
if (pc->drive[pdr->logical_unit_number].open_count)
return(TRUE);
/*ide_controller_reset(pc) */
ide_clear_voregs(pc); /* Clear virtual output registers */
if (!ide_wait_ready(pc, (int)TIMEOUT_RESET))
return(FALSE);
/* First issue a reset to the controller */
ide_wr_dig_out(pc, IDE_OUT_RESET);
/* sleep 2 ticks can be as little as 12 microseconds) */
ks_sleep(2);
ide_wr_dig_out(pc, 0);
/* Wait for busy to drop */
if (!ide_wait_not_busy(pc, (int)TIMEOUT_RESET))
return(FALSE);
/*DM: This check fails on CDROM. If it can't be made to work, how can we
do it only for hard disks? -- we haven't yet determined whether it's
a hard disk or ATAPI device. That's done below. That's why this is
done as a conditional, which is not great, since it means that this
call is not done for IDE drives either, if CDFS linked. */
/* Now perform drive diagnostics */
if (!ide_command_diags(pc))
return(FALSE);
/* Set the power value in case we are on a lowl power system */
/* Note: change the default value */
#if (IDE_USE_SET_FEATURES)
ide_set_features(pc, pdr->logical_unit_number, IDE_FEATURE_SETPERF, IDE_FEATURE_SETPERF_VALUE);
ide_set_features(pc, pdr->logical_unit_number, IDE_FEATURE_WCACHE_ON, 0x00);
ide_set_features(pc, pdr->logical_unit_number, IDE_FEATURE_DEFECT_ON, 0x00);
ide_set_features(pc, pdr->logical_unit_number, IDE_FEATURE_RLOOK_ON, 0x00);
#endif
/* Clear the atapi flag to start */
pc->drive[pdr->logical_unit_number].protocol = 0;
/* Use the ATA identify command. word 3 == # heads */
/* word 6 == sectors per track */
/* word 47 bits 0-7 ==s max sectors per read/write multiple */
/* word 49 bit 9 == if 1 supports logocal block addressing */
/* word 60-61 == number of user addressable LBA's in LBA mode */
pc->user_address = (PFBYTE) &buf[0];
if (!ide_command(IDE_CMD_IDENT, pc, pdr->logical_unit_number, 0, 0))
{
return(FALSE);
}
else
{
// BUG BUG - In CDFS system we should d diagnostics here.
pc->drive[pdr->logical_unit_number].media_descriptor=0xf8; /* assume hard disk media */
}
pc->drive[pdr->logical_unit_number].num_cylinders = to_WORD((byte *) &buf[1]);
heads = (byte) (to_WORD((byte *) &buf[3]));
sectors = (byte)(to_WORD((byte *) &buf[6]));
pc->drive[pdr->logical_unit_number].max_multiple = (byte) (to_WORD((byte *) &buf[47]) & 0xff);
if (to_WORD((byte *) &buf[49]) & 0x200)
{
pc->drive[pdr->logical_unit_number].supports_lba = TRUE;
/* Get highword low word of highest lba */
ltemp = to_WORD((byte *) &buf[61]);
ltemp <<= 16;
ltemp += to_WORD((byte *) &buf[60]);
ltemp2 = (dword) pc->drive[pdr->logical_unit_number].num_cylinders;
ltemp2 = (dword) (ltemp2 * heads);
ltemp2 = (dword) (ltemp2 * sectors);
tm_printf("lba fields 60:61 yield %ld h*c*n yield %ld\n", (dword)ltemp, (dword) ltemp2);
pc->drive[pdr->logical_unit_number].total_lba = ltemp;
}
else
{
pc->drive[pdr->logical_unit_number].supports_lba = FALSE;
pc->drive[pdr->logical_unit_number].total_lba = 0;
}
/* Our local view of the drive for block to track::sector::head xlations */
pc->drive[pdr->logical_unit_number].num_heads = heads;
pc->drive[pdr->logical_unit_number].sec_p_track = sectors;
pc->drive[pdr->logical_unit_number].sec_p_cyl = (word) heads;
pc->drive[pdr->logical_unit_number].sec_p_cyl = (word) (pc->drive[pdr->logical_unit_number].sec_p_cyl * sectors);
#if (USE_SETPARMS)
if (!ide_command_setparms(pc, pdr->logical_unit_number, heads, sectors))
{
return(FALSE);
}
#endif
/* 11-10-2000 - New code. Only do SETM on open. We had been doing it
on every read & write which caused a cache flush and slowed down io
Thanks to Paul Swan. */
if (pc->drive[pdr->logical_unit_number].max_multiple > 1)
{
if (!ide_command(IDE_CMD_SETM, pc, pdr->logical_unit_number, 0, 0))
{
return(FALSE);
}
}
pc->drive[pdr->logical_unit_number].open_count++; /*CDROM Support Jerry */
return(TRUE);
}
/* Read/write function: */
BOOLEAN ide_io(int driveno, dword sector, void *buffer, word count, BOOLEAN reading) /*__fn__*/
{
BOOLEAN ret_val;
PIDE_CONTROLLER pc;
DDRIVE *pdr;
pdr = pc_drno_to_drive_struct(driveno);
if (!pdr)
return(FALSE);
pc = &controller_s[pdr->controller_number];
if (!count || !pc) /* Must have a count */
return(FALSE);
ret_val = FALSE;
/* Set up a counter for data transfer (See ide_isr) */
pc->sectors_remaining = count;
// if (op == ERASING)
// {
// ret_val = ide_command(IDE_CMD_ERASES, pc, pdr->logical_unit_number, sector, count);
// }
if (reading)
{
pc->user_address = (PFBYTE) buffer;
/* Use read multiple command */
if ((count > 1) && (pc->drive[pdr->logical_unit_number].max_multiple > 1))
{
/* Call set multiple */
ret_val = ide_command_read_multiple(pc, pdr->logical_unit_number, sector, count);
if (!ret_val)
{
/* Set up a counter for data transfer (See ide_isr) */
pc->sectors_remaining = count;
goto non_multiple_read;
}
}
else
{
non_multiple_read:
do
{
pc->block_size = 1;
ret_val = ide_command(IDE_CMD_READS, pc, pdr->logical_unit_number, sector, count);
if (!ret_val)
{
break; /* Break out of do loop */
}
} while(!ret_val);
}
}
else // WRITING
{
pc->user_address = (PFBYTE) buffer;
if ( (count > 1) && (pc->drive[pdr->logical_unit_number].max_multiple > 1))
{
/* Try write multiple. */
ret_val = ide_command_write_multiple(pc, pdr->logical_unit_number, sector, count);
if (!ret_val)
{
pc->sectors_remaining = count;
goto non_multiple_write;
}
}
else
{
non_multiple_write:
pc->block_size = 1;
ret_val = ide_command(IDE_CMD_WRITES, pc, pdr->logical_unit_number, sector, count);
}
}
return(ret_val);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?