📄 bddrv.c
字号:
break;
case 2:
printf(" accelerated DRQ\n");
break;
}
#endif
printf("\n");
return 0;
}
/*****************************************************************************
*****************************************************************************/
static void ide_do_probe(ide_t *master, ide_t *slave)
{
unsigned short ioadr;
unsigned char byte1, byte2;
blkdev_t *blkdev;
ioadr = master->blkdev.io.adr[0];
/* poke interface */
DEBUG(printf("ide_do_probe: poking interface 0x%X\n", ioadr);)
outportb(ioadr + ATA_REG_COUNT, 0x55);
outportb(ioadr + ATA_REG_SECTOR, 0xAA);
byte1 = inportb(ioadr + ATA_REG_COUNT);
byte2 = inportb(ioadr + ATA_REG_SECTOR);
/* nothing there */
if(byte1 != 0x55 || byte2 != 0xAA)
{
DEBUG(printf("nothing there\n");)
return;
}
/* soft reset both drives on this I/F (selects master) */
DEBUG(printf("found something on I/F 0x%X, "
"doing soft reset...\n", ioadr);)
outportb(ioadr + ATA_REG_DEVCTRL, 0x06);
nsleep(400);
/* release soft reset AND enable interrupts from drive */
outportb(ioadr + ATA_REG_DEVCTRL, 0x00);
nsleep(400);
/* wait up to 2 seconds for status =
BUSY=0 READY=1 DF=? DSC=? DRQ=? CORR=? IDX=? ERR=0 */
blkdev = &master->blkdev;
if(ide_poll_status(ioadr, 2000, 0xC1, 0x40) != 0)
{
DEBUG(printf("ide_do_probe: no master on interface 0x%X\n",
ioadr);)
return;
}
/* identify master */
printf("master drive on interface 0x%X:\n", ioadr);
ide_identify(master);
/* select slave */
blkdev = &slave->blkdev;
if(ide_select_drive(ioadr, blkdev->unit) != 0)
{
DEBUG(printf("ide_do_probe: no slave on interface 0x%X\n",
ioadr);)
return;
}
/* identify slave */
ioadr = slave->blkdev.io.adr[0];
printf("slave drive on interface 0x%X:\n", ioadr);
ide_identify(slave);
}
/*****************************************************************************
*****************************************************************************/
static ide_t _drives[4];
static void ide_probe(void)
{
DEBUG(printf("ide_probe:\n");)
/* no DMA */
_drives[0].blkdev.io.dma = _drives[1].blkdev.io.dma = 0;
_drives[2].blkdev.io.dma = _drives[3].blkdev.io.dma = 0;
/* IRQs 14 and 15 */
_drives[0].blkdev.io.irq = _drives[1].blkdev.io.irq = 0x4000;
_drives[2].blkdev.io.irq = _drives[3].blkdev.io.irq = 0x8000;
/* 8 bytes at 0x1F0, 1 byte at 0x3F6 */
_drives[0].blkdev.io.adr[0] = _drives[1].blkdev.io.adr[0] = 0x1F0;
_drives[0].blkdev.io.span[0] = _drives[1].blkdev.io.span[0] = 8;
_drives[0].blkdev.io.adr[1] = _drives[1].blkdev.io.adr[1] = 0x3F6;
_drives[0].blkdev.io.span[1] = _drives[1].blkdev.io.span[1] = 1;
/* 8 bytes at 0x170, 1 byte at 0x3F6 (xxx - is 0x3F6 correct?) */
_drives[2].blkdev.io.adr[0] = _drives[3].blkdev.io.adr[0] = 0x170;
_drives[2].blkdev.io.span[0] = _drives[3].blkdev.io.span[0] = 8;
_drives[2].blkdev.io.adr[1] = _drives[3].blkdev.io.adr[1] = 0x3F6;
_drives[2].blkdev.io.span[1] = _drives[3].blkdev.io.span[1] = 1;
/* */
_drives[0].blkdev.unit = _drives[2].blkdev.unit = 0xA0;
_drives[1].blkdev.unit = _drives[3].blkdev.unit = 0xB0;
/* num_blks, bytes_per_blk, CHS, and ATA-/ATAPI-specific stuff
gets set by ide_do_probe() */
ide_do_probe(_drives + 0, _drives + 1);
ide_do_probe(_drives + 2, _drives + 3);
DEBUG(printf("\n");)
}
//////////////////////////////////////////////////////////////////////////////
// IDE (ATAPI) CD-ROM DRIVES
//////////////////////////////////////////////////////////////////////////////
/* the command byte within an ATAPI command packet */
#define ATAPI_CMD_START_STOP 0x1B /* eject/load */
#define ATAPI_CMD_READ10 0x28 /* read data sector(s) */
#define ATAPI_CMD_READTOC 0x43 /* read audio table-of-contents */
#define ATAPI_CMD_PLAY 0x47 /* play audio */
#define ATAPI_CMD_PAUSE 0x4B /* pause/continue audio */
/*****************************************************************************
xxx - finish
*****************************************************************************/
#pragma argsused
static int atapi_error(request_t *req)
{
static const char *key[] =
{
"no sense", "recovered error", "not ready", "medium error",
"hardware error", "illegal request", "unit attention", "data protect",
"?", "?", "?", "aborted command",
"?", "?", "miscompare", "?"
};
ide_t *dev;
unsigned short ioadr;
unsigned char temp;
DEBUG(printf("*** atapi_error: ***\n");)
dev = (ide_t *)req->dev;
ioadr = dev->blkdev.io.adr[0];
temp = inportb(ioadr + ATA_REG_STATUS);
printf("error bit=%u, ", temp & 1);
temp = inportb(ioadr + ATA_REG_ERROR);
printf("sense key=%u (%s)\n", temp >> 4, key[temp >>4]);
printf("MCR=%u, ", (temp & 8) >> 3);
printf("ABRT=%u, ", (temp & 4) >> 2);
printf("EOM=%u, ", (temp & 2) >> 1);
printf("ILI=%u\n", temp & 1);
(req->errors)++;
if(req->errors >= 3)
return -1;
return 0;
}
/*****************************************************************************
DRQ REASON "phase"
0 0 ATAPI_PH_ABORT
0 1 bad
0 2 bad
0 3 ATAPI_PH_DONE
8 0 ATAPI_PH_DATAOUT
8 1 ATAPI_PH_CMDOUT
8 2 ATAPI_PH_DATAIN
8 3 bad
b0 of REASON register is CoD or C/nD (0=data, 1=command)
b1 of REASON register is IO (0=out to drive, 1=in from drive)
*****************************************************************************/
#define ATAPI_PH_ABORT 0 /* other possible phases */
#define ATAPI_PH_DONE 3 /* (1, 2, 11) are invalid */
#define ATAPI_PH_DATAOUT 8
#define ATAPI_PH_CMDOUT 9
#define ATAPI_PH_DATAIN 10
static int atapi_cmd(request_t *req, unsigned char *pkt)
{
static const char *phase_name[] =
{
"ABORT", "invalid", "invalid", "DONE",
"can't happen", "can't happen", "can't happen", "can't happen",
"DATA OUT", "CMD OUT", "DATA IN", "invalid"
};
ide_t *dev;
blkdev_t *bdev;
unsigned short ioadr, num_blks, num_bytes;
int err;
unsigned char phase;
DEBUG(printf("atapi_cmd:\n");)
dev = (ide_t *)req->dev;
bdev = &dev->blkdev;
/* if the ide_t struct had a magic value in it, we could validate it here */
ioadr = bdev->io.adr[0];
/* select drive (xxx - need to do this?) */
err = ide_select_drive(ioadr, bdev->unit);
if(err != 0) /* xxx - no retry? */
return err;
AGAIN:
/* write ATA register file. Registers 2 (COUNT) and 3 (SECTOR) are not used */
outportb(ioadr + ATA_REG_FEAT, 0);
/* max. bytes to transfer on each DRQ */
outportb(ioadr + ATAPI_REG_LOCNT, 32768u);
outportb(ioadr + ATAPI_REG_HICNT, 32768u >> 8);
/* select drive and set LBA bit */
outportb(ioadr + ATA_REG_DRVHD, 0x40 | bdev->unit);
nsleep(400);
/* packet command byte */
outportb(ioadr + ATA_REG_CMD, ATA_CMD_PKT);
nsleep(400);
/* wait up to 0.5 seconds for status =
BUSY=0 READY=? DF=? DSC=? DRQ=1 CORR=? IDX=? ERR=?
i.e. await DRQ. This can be interrupt-driven for new drives,
instead of polled, but this code doesn't support it. */
if(ide_poll_status(ioadr, 500, 0x88, 0x08) != 0)
{
printf("atapi_cmd: error: drive did not "
"accept packet cmd byte\n");
goto ERR;
}
/* "4. When the Device is ready to accept the Command Packet,
the Device sets CoD and clears IO. DRQ shall then be asserted
simultaneous or prior to the de-assertion of BSY."
CoD=1, IO=0, DRQ=1 --> ATAPI_PH_CMDOUT */
phase = inportb(ioadr + ATA_REG_STATUS) & 0x08;
phase |= (inportb(ioadr + ATAPI_REG_REASON) & 3);
if(phase != ATAPI_PH_CMDOUT)
{
printf("atapi_cmd: error: drive did not "
"enter command out phase\n");
DEBUG(printf("atapi_cmd: ATAPI drive now in phase '%s'\n",
(phase < 12) ? phase_name[phase] : "can't happen");)
goto ERR;
}
/* write the 12-byte ATAPI packet, 2 bytes at a time */
outsw(ioadr + ATA_REG_DATA, (unsigned short *)pkt, 6);
/* main read loop */
for(;;)
{
/* wait up to 10 seconds for IRQ15 or IRQ14 */
if(await_interrupt(0xC000, 10000) == 0)
{
printf("atapi_cmd: error: "
"packet command timed out\n");
goto ERR;
}
/* get the ATAPI "phase".
Reading the status register here clears the interrupt. */
phase = inportb(ioadr + ATA_REG_STATUS) & 0x08;
phase |= (inportb(ioadr + ATAPI_REG_REASON) & 3);
DEBUG(printf("atapi_cmd: ATAPI drive now in phase '%s'\n",
(phase < 12) ? phase_name[phase] : "can't happen");)
/* DONE phase is OK, if we are truly done...
"10. When the Device is ready to present the status, the Device places
the completion status into the Status Register, sets CoD, IO, DRDY and
clears BSY, DRQ, prior to asserting INTRQ."
CoD=1, IO=1, DRQ=0 --> ATAPI_PH_DONE
xxx - we don't check if DRDY=1
"11. After detecting INTRQ & DRQ=0 the host reads the Status Register and
if necessary, the Error Register for the command completion status." */
if(phase == ATAPI_PH_DONE)
{
if(req->num_blks == 0)
return 0;
/* ...otherwise, it's an error */
printf("atapi_cmd: error: data shortage\n");
goto ERR;
}
/* besides DONE, DATA IN is the only valid phase
"7. When data is available, the Device:(1) places the byte count
of the data available into the Cylinder High and Low Registers,
(2) sets IO and clears CoD, (3) sets DRQ and clears BSY, (4) sets INTRQ."
IO=1, CoD=0, DRQ=1 --> ATAPI_PH_DATAIN */
/*else*/ if(phase != ATAPI_PH_DATAIN)
{
printf("atapi_cmd: error: "
"drive in unexpected phase\n");
ERR:
/* call atapi_error() if there is an error */
err = atapi_error(req);
if(err != 0)
return err;
/* ...else try again */
else
goto AGAIN;
}
/* read!
xxx - 16 is 32768 (max bytes per DRQ) / 2048 (bytes per sector) */
num_blks = min(req->num_blks, 16);
/* if num_blks drops to 0, then we just read and discard, no worries */
num_bytes = atapi_read_and_discard(ioadr, req->buf,
num_blks * bdev->bytes_per_blk);
num_blks = num_bytes / bdev->bytes_per_blk;
/* advance pointers */
req->buf += num_bytes;
req->num_blks -= num_blks;
req->blk += num_blks;
}
}
/*****************************************************************************
*****************************************************************************/
static int atapi_read_sectors(request_t *req)
{
unsigned char pkt[12] =
{
ATAPI_CMD_READ10, 0,
0, 0, 0, 0,
0, 0, 0,
0, 0, 0,
};
DEBUG(printf("atapi_read_sectors:\n");)
pkt[2]=req->blk >> 24;
pkt[3]=req->blk >> 16;
pkt[4]=req->blk >> 8;
pkt[5]=req->blk;
// pkt[6]=req->num_blks >> 16;
pkt[7]=req->num_blks >> 8;
pkt[8]=req->num_blks;
return atapi_cmd(req, pkt);
}
/*****************************************************************************
My CD-ROM is an old piece of shit Mitsumi, and doesn't support this
function, so I have no way of testing it. I don't even know if the
command bytes 0xBE (READ CD) or 0xD5 (READ CD MSF) are correct.
This is a guess, but: if atapi_mode_sense() displays
CD-DA commands: YES
then the drive can "rip" audio tracks.
*****************************************************************************/
static int atapi_rip_audio(request_t *req)
{
unsigned char pkt[12] =
{
#if 1
0xBE, 0,
0, 0, 0, 0, /* start LBA */
0, 0, 1, /* number of blocks */
#else
0xD5, 0, 0,
0, 0, 0,/* start MSF */
0, 0, 1,/* end MSF */
#endif
/* 9=10h flag bits
b7=synch field, b6:b5=header(s) code, b4=user data,
b3=EDC & ECC, b2:b1=error flag(s), b0=reserved */
0x10,
/* 10=0 b2:b0 = sub-channel data selection bits, other bits reserved */
0,
0,
};
DEBUG(printf("atapi_read_sectors:\n");)
pkt[2]=req->blk >> 24;
pkt[3]=req->blk >> 16;
pkt[4]=req->blk >> 8;
pkt[5]=req->blk;
pkt[6]=req->num_blks >> 16;
pkt[7]=req->num_blks >> 8;
pkt[8]=req->num_blks;
return atapi_cmd(req, pkt);
}
/*****************************************************************************
*****************************************************************************/
static int atapi_eject(ide_t *dev, bool load)
{
unsigned char pkt[12] =
{
ATAPI_CMD_START_STOP,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0
};
request_t req;
DEBUG(printf("atapi_eject:\n");)
memset(&req, 0, sizeof(req));
req.dev = dev;
pkt[4] = 2 + (load ? 1 : 0);
return atapi_cmd(&req, pkt);
}
/*****************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -