📄 temp.c
字号:
/*
Fixes/changes: - CD-ROM now works properly after ATAPI 'identify'.
Bugs/to do: - Write a new ataXfer() that
* allows reads to start in the middle of an ATA or ATAPI sector
(no odd writes until a buffer cache system is added)
(change Blk field of drivecmd [sectors] to Offset [bytes])
* allows odd-length Count in drivecmd
- Buffer cache
- Queued drive operations
- TEST drives on 2nd interface (0x170, IRQ15).
- Still some arbitrary timeout values.
- atapiCmd2() reads only 2048 bytes at a time. Is this normal?
- False detection of slave ATA drive with Seagate ST3144A master
(it's jumpered properly, and Linux and the BIOS work OK).
- Lots of blocking delays. This code is only partially interrupt-
driven, and not yet suitable for a high-performance OS.
- Code needs more error checking and error-recovery.
*****************************************************************************/
#include <stdlib.h> /* srand() rand() */
#include <conio.h> /* getch() */
#include <stdio.h> /* NULL printf() cprintf() */
//#define DEBUG
#ifdef DEBUG
/* extra debugging messages */
#define PRINT_DEBUG(X) X
#else
/* no debug msgs */
#define PRINT_DEBUG(X)
#endif/*
////////////////////////////////////////////////////////////////////////////
LOW LEVEL FUNCTIONS AND BASIC DEFINTIONS
////////////////////////////////////////////////////////////////////////////
*/
/* delay() inp() inpw() outp() outpw() */
#include <dos.h>
typedef unsigned char u8; /* 8 bits */
typedef unsigned short u16; /* 16 bits */
typedef unsigned long u32; /* 32 bits */
#define msleep delay
#define min(a,b) (((a) < (b)) ? (a) : (b))
int InterruptOccured;
/*****************************************************************************
name: nsleep
action: precision delay for Count nanoseconds
(not yet implemented)
*****************************************************************************/
void nsleep(unsigned Count)
{
delay(0);
}
/*****************************************************************************
name: insw
action: reads Count words (16-bit) from I/O port Adr to
memory location Data
*****************************************************************************/
void insw(unsigned Adr, u16 *Data, unsigned Count)
{
for(; Count; Count--)
*Data++=inpw(Adr);
}
/*****************************************************************************
name: outsw
action: writes Count words (16-bit) from memory location Data
to I/O port Adr
*****************************************************************************/
void outsw(unsigned Adr, u16 *Data, unsigned Count)
{
for(; Count; Count--)
outpw(Adr, *Data++);
}
/*****************************************************************************
name: dump
action: hexadecimal memory dump of Count bytes at Data
*****************************************************************************/
#define BPERL 16
/* byte/line for dump */
void dump(u8 *Data, unsigned Count)
{
unsigned Byte1, Byte2;
while(Count)
{
for(Byte1=0; Byte1 < BPERL; Byte1++, Count--)
{
if(Count == 0) break;
printf("%02X ", Data[Byte1]);
}
printf("\t");
for(Byte2=0; Byte2 < Byte1; Byte2++)
printf("%c", Data[Byte2] < ' ' ? '.' : Data[Byte2]);
printf("\n");
Data += BPERL;
}
}
/*****************************************************************************
name: awaitInterrupt
action: waits with Timeout (mS) until interrupt(s) given by bitmask
IRQMask occur
returns:nonzero Mask value if interrupt occured, zero if timeout
*****************************************************************************/
int awaitInterrupt(u16 IRQMask, unsigned Timeout)
{
u16 Intr;
for(; Timeout; Timeout--)
{
Intr=InterruptOccured & IRQMask;
if(Intr) break;
msleep(1);
}
/* XXX - blocking delay - fix */
if(Timeout == 0) return(0);
InterruptOccured &= ~Intr;
return(Intr);
}
/*
////////////////////////////////////////////////////////////////////////////
ATA/ATAPI STUFF
////////////////////////////////////////////////////////////////////////////
*/
/* Delays from Linux ide.h (milliseconds) */
#define WAIT_READY 30 /* RDY asserted, use 5000 for notebook/APM */
#define WAIT_ID 30000 /* ATA device responds to 'identify' */
#define WAIT_PID 3 /* ATAPI device responds to 'identify' */
#define WAIT_CMD 10000 /* IRQ occurs in response to command */
#define WAIT_DRQ 20 /* DRQ asserted after ATA_CMD_WR(MUL) */
/* 'Cmd' field of 'drivecmd' structure */
#define DRV_CMD_RD 1
#define DRV_CMD_WR 2
/* ATA or ATAPI command structure */
typedef struct{
u32 Blk; /* in SECTORS */
u32 Count; /* in BYTES */
u8 Dev, Cmd, *Data;
} drivecmd;
/* ATA sector size */
#define ATA_LG_SECTSIZE 9 /* 512 byte ATA drive sectors */
#define ATA_SECTSIZE (1 << (ATA_LG_SECTSIZE)) /* ATAPI sector size */
#define ATAPI_LG_SECTSIZE 11 /* 2K CD-ROM sectors */
#define ATAPI_SECTSIZE (1 << (ATAPI_LG_SECTSIZE))
/* ATA drive command bytes */
#define ATA_CMD_RD 0x20 /* read one sector */
#define ATA_CMD_WR 0x30 /* write one sector */
#define ATA_CMD_PKT 0xA0 /* ATAPI packet cmd */
#define ATA_CMD_PID 0xA1 /* ATAPI identify */
#define ATA_CMD_RDMUL 0xC4 /* read multiple sectors */
#define ATA_CMD_WRMUL 0xC5 /* write multiple sectors */
#define ATA_CMD_ID 0xEC /* ATA identify */
/* ATA drive flags */
#define ATA_FLG_ATAPI 0x0001 /* ATAPI drive */
#define ATA_FLG_LBA 0x0002 /* LBA-capable */
#define ATA_FLG_DMA 0x0004 /* DMA-capable */
/* ATA/ATAPI drive register file */
#define ATA_REG_DATA 0 /* data (16-bit) */
#define ATA_REG_FEAT 1 /* write: feature reg */
#define ATA_REG_ERR ATA_REG_FEAT /* read: error */
#define ATA_REG_CNT 2 /* ATA: sector count */
#define ATA_REG_REASON ATA_REG_CNT /* ATAPI: interrupt reason */
#define ATA_REG_SECT 3 /* sector */
#define ATA_REG_LOCYL 4 /* ATA: LSB of cylinder */
#define ATA_REG_LOCNT ATA_REG_LOCYL /* ATAPI: LSB of transfer count */
#define ATA_REG_HICYL 5 /* ATA: MSB of cylinder */
#define ATA_REG_HICNT ATA_REG_HICYL /* ATAPI: MSB of transfer count */
#define ATA_REG_DRVHD 6 /* drive select; head */
#define ATA_REG_CMD 7 /* write: drive command */
#define ATA_REG_STAT 7 /* read: status and error flags */
#define ATA_REG_SLCT 0x206 /* write: device control */
#define ATA_REG_ALTST 0x206 /* read: alternate status/error */
typedef struct /* 'identify' structure, as per ANSI ATA2 rev.2f spec */
{
u16 Config, PhysCyls, Res2, PhysHeads, UnfBytesPerTrack;
u16 UnfBytesPerSect, PhysSects, Vendor0, Vendor1, Vendor2;
u8 SerialNum[20];
u16 BufType, BufSize, ECCBytes; u8 FirmwareRev[8], Model[40], MaxMult, Vendor3; u16 DwordIO;
u8 Vendor4, Capability; u16 Res50;
u8 Vendor5, PIOMode, Vendor6, DMAMode; u16 LogValid, LogCyls, LogHeads, LogSects;
u32 TotalSects; u8 MultSect, MultSectValid;
u32 LBASects;
u16 DMAInfoSingle, DMAInfoMult, EIDEPIOModes;
u16 EIDEDMAMin, EIDEDMATime, EIDEPIO, EIDEPIOIORdy;
/* This fixed the drive-goes-north-after-ATAPI-identify bug
u16 Res69, Res70; } ataid; */
u16 Res[187];
} ataid;
/* generalized drive info structure */
typedef struct{
u16 Flags;
u8 DrvSel;
/* ATA, ATAPI only (LUN for SCSI?) */
u8 MultSect;
/* ATA only */
u16 Sects, Heads, Cyls;
/* CHS ATA only */
u16 IOAdr;
} drive;
drive Drive[4];
/*****************************************************************************
name: ataSelect
*****************************************************************************/
int ataSelect(unsigned int IOAdr, unsigned char Sel)
{
unsigned char Temp;
Temp=inp(IOAdr + ATA_REG_DRVHD);
if(((Temp ^ Sel) & 0x10) == 0) return(0); /* already selected */
outp(IOAdr + ATA_REG_DRVHD, Sel);
nsleep(400);
for(Temp=WAIT_READY; Temp; Temp--)
{
if((inp(IOAdr + ATA_REG_STAT) & 0x80) == 0) break;
msleep(1);
}
/* this _must_ be polled, I guess (sigh) */
return(Temp == 0);
}
/*****************************************************************************
name: ataProbe2
*****************************************************************************/
void ataProbe2(unsigned char WhichDrive, unsigned char DrvSel)
{
unsigned char Temp, Temp1, Temp2;
unsigned int IOAdr;
ataid DriveInfo;
IOAdr=Drive[WhichDrive].IOAdr;
ataSelect(IOAdr, DrvSel);
Temp1=inp(IOAdr + ATA_REG_CNT);
Temp2=inp(IOAdr + ATA_REG_SECT);
if (Temp1 != 0x01 || Temp2 != 0x01)
{
printf("nothing there\n");
NO_DRIVE:
Drive[WhichDrive].IOAdr=0;
return;
}
Temp1=inp(IOAdr + ATA_REG_LOCYL);
Temp2=inp(IOAdr + ATA_REG_HICYL);
Temp=inp(IOAdr + ATA_REG_STAT);
InterruptOccured=0;
if (Temp1 == 0x14 && Temp2 == 0xEB)
{
printf("ATAPI CD-ROM, ");
Drive[WhichDrive].Flags |= ATA_FLG_ATAPI;
// issue ATAPI 'identify drive' command
Temp1=ATA_CMD_PID;
outp(IOAdr + ATA_REG_CMD, Temp1);
Temp=(unsigned char)WAIT_PID;
}
else
if(Temp1 == 0 && Temp2 == 0 && Temp)
{
printf("ATA hard drive, ");
// issue ATA 'identify drive' command
Temp1=ATA_CMD_ID;
outp(IOAdr + ATA_REG_CMD, Temp1);
Temp=(unsigned char)WAIT_ID;
}
else
{
printf("unknown drive type\n");
goto NO_DRIVE;
}
// ATA or ATAPI: get results of of identify
nsleep(400);
if(awaitInterrupt(0xC000, Temp) == 0)
// XXX - could be old drive that doesn't support 'identify'.Read geometry from partition table? Use (* gag *) CMOS?
{
printf("'identify' failed\n");
goto NO_DRIVE;
}
// grab info returned by 'identify'
(void)inp(IOAdr + ATA_REG_STAT);
// for ATAPI CD-ROM, you MUST read 512 bytes here, ordrive will go comatose
insw(IOAdr + ATA_REG_DATA, (u16 *)&DriveInfo, sizeof(DriveInfo) / 2);
Temp2=1;
if(Temp1 == ATA_CMD_PID)
// model name is not byte swapped for NEC, Mitsumi, and Pioneer drives
{
if((DriveInfo.Model[0] == 'N' && DriveInfo.Model[1] == 'E') ||
(DriveInfo.Model[0] == 'F' && DriveInfo.Model[1] == 'X') ||
(DriveInfo.Model[0] == 'P' && DriveInfo.Model[1] == 'i'))
Temp2=0;
}
for(Temp=0; Temp < 40; Temp += 2)
{
printf("%c", DriveInfo.Model[Temp ^ Temp2]);
printf("%c", DriveInfo.Model[Temp ^ Temp2 ^ 1]);
}
printf("\n"
"CHS=%u:%u:%u, ", DriveInfo.PhysCyls, DriveInfo.PhysHeads,
DriveInfo.PhysSects);
Drive[WhichDrive].Sects=DriveInfo.PhysSects;
Drive[WhichDrive].Heads=DriveInfo.PhysHeads;
Drive[WhichDrive].Cyls=DriveInfo.PhysCyls;
if(DriveInfo.Capability & 1)
{
printf("DMA, ");
Drive[WhichDrive].Flags |= ATA_FLG_DMA;
}
if(DriveInfo.Capability & 2)
{
printf("LBA, ");
Drive[WhichDrive].Flags |= ATA_FLG_LBA;
}
// By Dobbs, I'll figure this out yet. Linux ide.c requires
// (DriveInfo.MultSectValid & 1) && DriveInfo.MultSect
// The magic value then comes from DriveInfo.MaxMult
// QUANTUM FIREBALL ST2.1A MaxMult=16 MultSect=16 MultSectValid=1
// Conner Peripherals 850MB - CFS850A MaxMult=16 MultSect=0 MultSectValid=1
// (Seagate) st3144AT MaxMult=0 MultSect=0 MultSectValid=0
if((DriveInfo.MultSectValid & 1) && DriveInfo.MultSect)
{
Temp=DriveInfo.MaxMult;
printf("MaxMult=%u, ", Temp);
}
else
Temp=1;
Drive[WhichDrive].MultSect=Temp;
printf("%uK cache\n", DriveInfo.BufSize >> 1);
#ifdef DEBUG
printf("\n"
"Config=0x%hX, PhysCyls=%hu, Res2=%hu, PhysHeads=%hu, "
"UnfBytesPerTrack=%hu\n"
"PhysSects=%hu, Vendor0=%hu, Vendor1=%hu, "
"Vendor2=%hu\n",DriveInfo.Config, DriveInfo.PhysCyls, DriveInfo.Res2, DriveInfo.PhysHeads,DriveInfo.UnfBytesPerTrack, DriveInfo.PhysSects, DriveInfo.Vendor0,DriveInfo.Vendor1, DriveInfo.Vendor2);
printf("\n"
"BufType=%hu, BufSize=%hu, ECCBytes=%hu, MaxMult=%hu, Vendor3=%hu, "
"DwordIO=%hu\n"
"Vendor4=%hu, Capability=0x%hX\n",DriveInfo.BufType, DriveInfo.BufSize, DriveInfo.ECCBytes, DriveInfo.MaxMult,DriveInfo.Vendor3, DriveInfo.DwordIO, DriveInfo.Vendor4, DriveInfo.Capability);
printf("\n"
"Res50=%hu, Vendor5=%hu, PIOMode=%hu, Vendor6=%hu, DMAMode=%hu, "
"LogValid=%hu, LogCyls=%hu\n"
"LogHeads=%hu, LogSects=%hu, ",DriveInfo.Res50, DriveInfo.Vendor5, DriveInfo.PIOMode, DriveInfo.Vendor6,DriveInfo.DMAMode, DriveInfo.LogValid, DriveInfo.LogCyls, DriveInfo.LogHeads,DriveInfo.LogSects);
printf("TotalSects=%lu, MultSect=%hu\n",DriveInfo.TotalSects, DriveInfo.MultSect);printf("MultSectValid=%hu\n"
"LBASects=%lu\n",DriveInfo.MultSectValid, DriveInfo.LBASects);
printf("\n"
"DMAInfoSingle=%hu, DMAInfoMult=%hu, EIDEPIOModes=%hu, EIDEDMAMin=%hu"
"\n"
"EIDEDMATime=%hu, EIDEPIO=%hu, EIDEPIOIORdy=%hu\n" "\n",DriveInfo.DMAInfoSingle, DriveInfo.DMAInfoMult, DriveInfo.EIDEPIOModes,DriveInfo.EIDEDMAMin, DriveInfo.EIDEDMATime, DriveInfo.EIDEPIO,DriveInfo.EIDEPIOIORdy);
#endif
}
char soft_reset(unsigned int IOAdr)
{
unsigned char Temp;
PRINT_DEBUG(printf("ataProbe: found something on I/F 0x%03X, "
"doing soft reset...\n", IOAdr);)
outp(IOAdr + ATA_REG_SLCT, 0x0E);
nsleep(400);// release soft reset AND enable interrupts from drive
outp(IOAdr + ATA_REG_SLCT, 0x08);
nsleep(400);// wait for master
for(Temp=2000; Temp; Temp--) // XXX - why 2000?
{
if((inp(IOAdr + ATA_REG_STAT) & 0x80) == 0) break;
msleep(1);
}
// XXX - blocking delay
if(Temp == 0)
{
PRINT_DEBUG(printf("ataProbe: no master on I/F 0x%03X\n", IOAdr);)
return -1;
}
return 0;
}
/*****************************************************************************
name: ataProbe
*****************************************************************************/
void ataProbe(void)
{
unsigned char Temp, Temp1, Temp2, WhichDrive;
unsigned int IOAdr;
printf("ataProbe:\n");
/* set initial values */
Drive[0].DrvSel=Drive[2].DrvSel=0xA0;
Drive[1].DrvSel=Drive[3].DrvSel=0xB0;
Drive[0].IOAdr=Drive[1].IOAdr=0x1F0;
Drive[2].IOAdr=Drive[3].IOAdr=0x170;
for (WhichDrive=0; WhichDrive < 4; WhichDrive += 2)
{
IOAdr=Drive[WhichDrive].IOAdr;
/* poke at the interface to see if anything's there */
PRINT_DEBUG(printf("ataProbe: poking interface 0x%03X\n", IOAdr);)
outp(IOAdr + ATA_REG_CNT, 0x55);
outp(IOAdr + ATA_REG_SECT, 0xAA);
Temp1=inp(IOAdr + ATA_REG_CNT);
Temp2=inp(IOAdr + ATA_REG_SECT);
if(Temp1 != 0x55 || Temp2 != 0xAA)
/* no master: clobber both master and slave */
NO_DRIVES:
{
Drive[WhichDrive + 1].IOAdr=Drive[WhichDrive].IOAdr=0;
continue;
}
/* soft reset both drives on this I/F (selects master) */
if (!soft_reset(IOAdr)) { };
// goto NO_DRIVES;
/* identify master */
printf(" hd%1u (0x%03X, master): ", WhichDrive, IOAdr);
ataProbe2(WhichDrive, 0xA0);
if (!soft_reset(IOAdr)) { };
// goto NO_DRIVES;
/* identify slave */
printf(" hd%1u (0x%03X, slave): ", WhichDrive + 1, IOAdr);
ataProbe2(WhichDrive + 1, 0xB0);
}
}
/*****************************************************************************
name: ataCmd2
*****************************************************************************/
void ataCmd2(drivecmd *Cmd, u8 Count, u8 CmdByte)
{
u8 Sect, DrvHd; u16 Cyl, IOAdr; u32 Temp;
IOAdr=Drive[Cmd->Dev].IOAdr;
/* compute CHS or LBA register values */
Temp=Cmd->Blk;
if(Drive[Cmd->Dev].Flags & ATA_FLG_LBA)
{
Sect=Temp;
/* 28-bit sector adr: low byte */
Cyl=Temp >> 8;
/* middle bytes */
DrvHd=Temp >> 24;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -