601 lines
17 KiB
HolyC
601 lines
17 KiB
HolyC
U0 ATABlkSel(CBlkDev *bd, I64 blk, I64 cnt) {
|
|
if (bd->type != BDT_ATAPI && bd->base1)
|
|
OutU8(bd->base1 + ATAR1_CTRL, 0x8);
|
|
if (bd->flags & BDF_EXT_SIZE) { // 48 Bit LBA?
|
|
OutU8(bd->base0 + ATAR0_NSECT, cnt.u8[1]);
|
|
OutU8(bd->base0 + ATAR0_SECT, blk.u8[3]);
|
|
OutU8(bd->base0 + ATAR0_LCYL, blk.u8[4]);
|
|
OutU8(bd->base0 + ATAR0_HCYL, blk.u8[5]);
|
|
OutU8(bd->base0 + ATAR0_NSECT, cnt);
|
|
OutU8(bd->base0 + ATAR0_SECT, blk);
|
|
OutU8(bd->base0 + ATAR0_LCYL, blk.u8[1]);
|
|
OutU8(bd->base0 + ATAR0_HCYL, blk.u8[2]);
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
} else { // 28 Bit LBA
|
|
OutU8(bd->base0 + ATAR0_NSECT, cnt);
|
|
OutU8(bd->base0 + ATAR0_SECT, blk);
|
|
OutU8(bd->base0 + ATAR0_LCYL, blk.u8[1]);
|
|
OutU8(bd->base0 + ATAR0_HCYL, blk.u8[2]);
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4 | blk.u8[3]);
|
|
}
|
|
}
|
|
|
|
Bool ATAWaitNotBUSY(CBlkDev *bd, F64 timeout) {
|
|
I64 i;
|
|
do {
|
|
for (i = 0; i < 3; i++)
|
|
if (!(InU8(bd->base0 + ATAR0_STAT) & ATAS_BSY))
|
|
return TRUE;
|
|
Yield;
|
|
} while (!(0 < timeout < tS));
|
|
return FALSE;
|
|
}
|
|
|
|
Bool ATAWaitDRQ(CBlkDev *bd, F64 timeout) {
|
|
I64 i;
|
|
do {
|
|
for (i = 0; i < 3; i++)
|
|
if (InU8(bd->base0 + ATAR0_STAT) & ATAS_DRQ)
|
|
return TRUE;
|
|
Yield;
|
|
} while (!(0 < timeout < tS));
|
|
return FALSE;
|
|
}
|
|
|
|
Bool ATANop(CBlkDev *bd, F64 timeout) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_FEAT, 0);
|
|
OutU8(bd->base0 + ATAR0_CMD, ATA_NOP);
|
|
return ATAWaitNotBUSY(bd, timeout);
|
|
}
|
|
|
|
U0 ATACmd(CBlkDev *bd, U8 cmd) {
|
|
OutU8(bd->base0 + ATAR0_FEAT, 0);
|
|
OutU8(bd->base0 + ATAR0_CMD, cmd);
|
|
bd->last_time = tS;
|
|
PortNop;
|
|
}
|
|
|
|
Bool ATAGetRes(CBlkDev *bd, F64 timeout, U8 *buf, I64 cnt, I64 _avail,
|
|
Bool one_read) {
|
|
I64 avail, overflow;
|
|
bd->flags &= ~BDF_LAST_WAS_WRITE;
|
|
MemSet(buf, 0, cnt);
|
|
while (cnt > 0) {
|
|
if (!ATAWaitDRQ(bd, timeout))
|
|
return FALSE;
|
|
if (_avail)
|
|
avail = _avail;
|
|
else
|
|
avail = InU8(bd->base0 + ATAR0_HCYL) << 8 + InU8(bd->base0 + ATAR0_LCYL);
|
|
if (avail) {
|
|
if (avail > cnt) {
|
|
overflow = avail - cnt;
|
|
avail = cnt;
|
|
} else
|
|
overflow = 0;
|
|
if (avail & 2)
|
|
RepInU16(buf, avail >> 1, bd->base0 + ATAR0_DATA);
|
|
else
|
|
RepInU32(buf, avail >> 2, bd->base0 + ATAR0_DATA);
|
|
cnt -= avail;
|
|
buf += avail;
|
|
while (overflow > 0) {
|
|
InU16(bd->base0 + ATAR0_DATA);
|
|
overflow -= 2;
|
|
if (0 < timeout < tS)
|
|
return FALSE;
|
|
}
|
|
if (one_read)
|
|
break;
|
|
} else
|
|
Yield;
|
|
}
|
|
return ATAWaitNotBUSY(bd, timeout);
|
|
}
|
|
|
|
Bool ATAPIWritePktWord(CBlkDev *bd, F64 timeout, ...) {
|
|
I64 i;
|
|
for (i = 0; i < argc; i++) {
|
|
if (!ATAWaitDRQ(bd, timeout))
|
|
return FALSE;
|
|
OutU16(bd->base0 + ATAR0_DATA, EndianU16(argv[i]));
|
|
bd->last_time = tS;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
Bool ATAPISetMaxSpeed(CBlkDev *bd) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, 0);
|
|
OutU8(bd->base0 + ATAR0_HCYL, 0);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0xBB00, 0xFFFF, 0xFFFF, 0, 0, 0);
|
|
return ATAWaitNotBUSY(bd, 0);
|
|
}
|
|
|
|
Bool ATAPISeek(CBlkDev *bd, I64 native_blk) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, 0);
|
|
OutU8(bd->base0 + ATAR0_HCYL, 0);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0x2B00, native_blk >> 16, native_blk, 0, 0, 0);
|
|
return ATAWaitNotBUSY(bd, 0);
|
|
}
|
|
|
|
Bool ATAPIStartStop(CBlkDev *bd, F64 timeout, Bool start) {
|
|
I64 i;
|
|
if (start)
|
|
i = 0x100;
|
|
else
|
|
i = 0;
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
ATACmd(bd, ATA_PACKET);
|
|
// Start/Stop
|
|
if (ATAPIWritePktWord(bd, timeout, 0x1B00, 0, i, 0, 0, 0))
|
|
return ATAWaitNotBUSY(bd, timeout);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
I64 ATAGetDevId(CBlkDev *bd, F64 timeout, Bool keep_id_record) {
|
|
I64 res = BDT_NULL;
|
|
U16 *id_record = NULL;
|
|
if (bd->type != BDT_ATAPI && bd->base1)
|
|
OutU8(bd->base1 + ATAR1_CTRL, 0x8);
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
ATACmd(bd, ATA_ID_DEV);
|
|
ATAWaitNotBUSY(bd, timeout);
|
|
if (InU8(bd->base0 + ATAR0_STAT) & ATAS_ERR)
|
|
res = BDT_ATAPI;
|
|
else {
|
|
id_record = ACAlloc(512);
|
|
if (ATAGetRes(bd, timeout, id_record, 512, 512, FALSE))
|
|
res = BDT_ATA;
|
|
else {
|
|
Free(id_record);
|
|
id_record = NULL;
|
|
}
|
|
}
|
|
if (keep_id_record) {
|
|
Free(bd->dev_id_record);
|
|
bd->dev_id_record = id_record;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
I64 ATAReadNativeMax(CBlkDev *bd, F64 timeout) { // Returns zero on err
|
|
I64 res = 0;
|
|
Bool okay = TRUE;
|
|
if (bd->type == BDT_ATAPI) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
ATACmd(bd, ATA_DEV_RST);
|
|
if (!ATAWaitNotBUSY(bd, 0))
|
|
okay = FALSE;
|
|
} else {
|
|
while (InU8(bd->base0 + ATAR0_STAT) & ATAS_BSY) {
|
|
if (bd->flags & BDF_LAST_WAS_WRITE)
|
|
OutU16(bd->base0 + ATAR0_DATA, 0);
|
|
else
|
|
InU16(bd->base0 + ATAR0_DATA);
|
|
Yield;
|
|
if (0 < timeout < tS)
|
|
return FALSE;
|
|
}
|
|
if (ATAGetDevId(bd, timeout, TRUE) == BDT_NULL)
|
|
okay = FALSE;
|
|
else
|
|
BEqu(&bd->flags, BDf_EXT_SIZE, Bt(&bd->dev_id_record[86], 10));
|
|
}
|
|
if (okay) {
|
|
if (bd->flags & BDF_EXT_SIZE && bd->base1) {
|
|
OutU8(bd->base1 + ATAR1_CTRL, 0x8);
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
ATACmd(bd, ATA_READ_NATIVE_MAX_EXT);
|
|
if (ATAWaitNotBUSY(bd, timeout)) {
|
|
res.u8[0] = InU8(bd->base0 + ATAR0_SECT);
|
|
res.u8[1] = InU8(bd->base0 + ATAR0_LCYL);
|
|
res.u8[2] = InU8(bd->base0 + ATAR0_HCYL);
|
|
|
|
OutU8(bd->base1 + ATAR1_CTRL, 0x80);
|
|
res.u8[3] = InU8(bd->base0 + ATAR0_SECT);
|
|
res.u8[4] = InU8(bd->base0 + ATAR0_LCYL);
|
|
res.u8[5] = InU8(bd->base0 + ATAR0_HCYL);
|
|
|
|
if (res >> 24 == res & 0xFFFFFF) { // Kludge to make QEMU work
|
|
bd->flags &= ~BDF_EXT_SIZE;
|
|
res &= 0xFFFFFF;
|
|
}
|
|
}
|
|
} else {
|
|
if (bd->type != BDT_ATAPI && bd->base1)
|
|
OutU8(bd->base1 + ATAR1_CTRL, 0x8);
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
ATACmd(bd, ATA_READ_NATIVE_MAX);
|
|
if (ATAWaitNotBUSY(bd, timeout)) {
|
|
res.u8[0] = InU8(bd->base0 + ATAR0_SECT);
|
|
res.u8[1] = InU8(bd->base0 + ATAR0_LCYL);
|
|
res.u8[2] = InU8(bd->base0 + ATAR0_HCYL);
|
|
res.u8[3] = InU8(bd->base0 + ATAR0_SEL) & 0xF;
|
|
}
|
|
}
|
|
}
|
|
return bd->max_blk = res;
|
|
}
|
|
|
|
I64 ATAPIReadCapacity(
|
|
CBlkDev *bd,
|
|
I64 *_blk_size = NULL) { // Supposedly this can return a res +/- 75 sects.
|
|
// Error might just be for music.
|
|
Bool unlock = BlkDevLock(bd);
|
|
U32 buf[2];
|
|
if (ATAWaitNotBUSY(bd, 0)) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, 8);
|
|
OutU8(bd->base0 + ATAR0_HCYL, 0);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0x2500, 0, 0, 0, 0, 0);
|
|
if (!ATAGetRes(bd, 0, buf, 8, 0, TRUE))
|
|
buf[0] = buf[1] = 0;
|
|
} else
|
|
buf[0] = buf[1] = 0;
|
|
|
|
if (unlock)
|
|
BlkDevUnlock(bd);
|
|
if (_blk_size)
|
|
*_blk_size = EndianU32(buf[1]);
|
|
return EndianU32(buf[0]);
|
|
}
|
|
|
|
CATAPITrack *ATAPIReadTrackInfo(CBlkDev *bd, I64 blk) {
|
|
CATAPITrack *res = CAlloc(sizeof(CATAPITrack));
|
|
Bool unlock = BlkDevLock(bd);
|
|
if (ATAWaitNotBUSY(bd, 0)) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, sizeof(CATAPITrack) & 0xFF);
|
|
OutU8(bd->base0 + ATAR0_HCYL, sizeof(CATAPITrack) >> 8);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0x5200, blk.u16[1], blk.u16[0],
|
|
(sizeof(CATAPITrack) & 0xFF00) >> 8,
|
|
(sizeof(CATAPITrack) & 0x00FF) << 8, 0);
|
|
if (!ATAGetRes(bd, 0, res, sizeof(CATAPITrack), 0, TRUE)) {
|
|
Free(res);
|
|
res = NULL;
|
|
}
|
|
} else {
|
|
Free(res);
|
|
res = NULL;
|
|
}
|
|
if (unlock)
|
|
BlkDevUnlock(bd);
|
|
return res;
|
|
}
|
|
|
|
Bool ATAInit(CBlkDev *bd) {
|
|
Bool unlock = BlkDevLock(bd), okay = FALSE;
|
|
|
|
if (bd->type == BDT_ATAPI)
|
|
bd->flags &= ~BDF_EXT_SIZE;
|
|
else
|
|
bd->flags |= BDF_EXT_SIZE;
|
|
|
|
if (ATAReadNativeMax(bd, tS + 0.1)) {
|
|
ATABlkSel(bd, bd->max_blk, 0);
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
ATACmd(bd, ATA_SET_MAX_EXT);
|
|
else
|
|
ATACmd(bd, ATA_SET_MAX);
|
|
if (ATAWaitNotBUSY(bd, 0)) {
|
|
okay = TRUE;
|
|
if (bd->type == BDT_ATAPI) {
|
|
if (ATAPIStartStop(bd, 0, TRUE)) {
|
|
if (!ATAPISetMaxSpeed(bd))
|
|
okay = FALSE;
|
|
} else
|
|
okay = FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (unlock)
|
|
BlkDevUnlock(bd);
|
|
return okay;
|
|
}
|
|
|
|
Bool ATAPIWaitReady(CBlkDev *bd, F64 timeout) {
|
|
do {
|
|
if (!ATAWaitNotBUSY(bd, timeout) || !ATANop(bd, timeout) ||
|
|
!ATAPIStartStop(bd, timeout, TRUE))
|
|
return FALSE;
|
|
if (InU8(bd->base0 + ATAR0_STAT) & ATAS_DRDY &&
|
|
!InU8(bd->base0 + ATAR0_FEAT))
|
|
;
|
|
return TRUE;
|
|
ATAInit(bd);
|
|
Yield;
|
|
} while (!(0 < timeout < tS));
|
|
return FALSE;
|
|
}
|
|
|
|
U0 ATAReadBlks(CBlkDev *bd, U8 *buf, I64 blk, I64 cnt) {
|
|
I64 retries = 3;
|
|
Bool unlock = BlkDevLock(bd);
|
|
|
|
retry:
|
|
ATABlkSel(bd, blk, cnt);
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
ATACmd(bd, ATA_READ_MULTI_EXT);
|
|
else
|
|
ATACmd(bd, ATA_READ_MULTI);
|
|
if (!ATAGetRes(bd, tS + 1.0, buf, cnt * bd->blk_size, BLK_SIZE, FALSE)) {
|
|
if (retries--) {
|
|
ATAWaitNotBUSY(bd, 0);
|
|
goto retry;
|
|
} else
|
|
throw('BlkDev');
|
|
}
|
|
|
|
blkdev.read_cnt += (cnt * bd->blk_size) >> BLK_SIZE_BITS;
|
|
if (unlock)
|
|
BlkDevUnlock(bd);
|
|
}
|
|
|
|
I64 ATAProbe(I64 base0, I64 base1, I64 unit) {
|
|
CBlkDev bd;
|
|
MemSet(&bd, 0, sizeof(CBlkDev));
|
|
bd.type = BDT_ATAPI;
|
|
bd.base0 = base0;
|
|
bd.base1 = base1;
|
|
bd.unit = unit;
|
|
bd.blk_size = DVD_BLK_SIZE;
|
|
return ATAGetDevId(&bd, tS + 0.1, FALSE);
|
|
}
|
|
|
|
Bool ATAPIReadBlks2(CBlkDev *bd, F64 timeout, U8 *buf, I64 native_blk, I64 cnt,
|
|
Bool lock) {
|
|
Bool res = FALSE, unlock;
|
|
if (cnt <= 0)
|
|
return FALSE;
|
|
if (lock)
|
|
unlock = BlkDevLock(bd);
|
|
if (ATAPIWaitReady(bd, timeout)) {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, bd->blk_size);
|
|
OutU8(bd->base0 + ATAR0_HCYL, bd->blk_size.u8[1]);
|
|
ATACmd(bd, ATA_PACKET);
|
|
if (ATAPIWritePktWord(bd, timeout, 0xA800, native_blk.u16[1], native_blk,
|
|
cnt.u16[1], cnt, 0) &&
|
|
ATAGetRes(bd, timeout, buf, cnt * bd->blk_size, 0, FALSE)) {
|
|
blkdev.read_cnt += (cnt * bd->blk_size) >> BLK_SIZE_BITS;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
// ATAPIStartStop(bd,0,FALSE);
|
|
if (lock && unlock)
|
|
BlkDevUnlock(bd);
|
|
return res;
|
|
}
|
|
|
|
U0 ATAPIReadBlks(CBlkDev *bd, U8 *buf, I64 blk, I64 cnt) {
|
|
CDrv *dv = Let2Drv(bd->first_drv_let);
|
|
I64 retry, spc = bd->blk_size >> BLK_SIZE_BITS, n, blk2,
|
|
l2 = bd->max_reads << 1 + spc << 1;
|
|
U8 *dvd_buf = MAlloc(l2 << BLK_SIZE_BITS);
|
|
if (cnt > 0) {
|
|
if (blk <= bd->max_reads)
|
|
blk2 = 0;
|
|
else
|
|
blk2 = FloorU64(blk - bd->max_reads, spc);
|
|
if (blk2 + l2 > dv->size + dv->drv_offset)
|
|
l2 = dv->size + dv->drv_offset - blk2;
|
|
n = (l2 + spc - 1) / spc;
|
|
|
|
retry = 4;
|
|
while (--retry)
|
|
if (ATAPIReadBlks2(bd, tS + 7.0 + 0.004 * n, dvd_buf, blk2 / spc, n,
|
|
TRUE))
|
|
// n is 0x800 if max_reads. Up to 8 additional seconds
|
|
break;
|
|
if (!retry)
|
|
ATAPIReadBlks2(bd, 0, dvd_buf, blk2 / spc, n, TRUE);
|
|
if (bd->flags & BDF_READ_CACHE)
|
|
DskCacheAdd(dv, dvd_buf, blk2, n * spc);
|
|
MemCpy(buf, dvd_buf + (blk - blk2) << BLK_SIZE_BITS, cnt << BLK_SIZE_BITS);
|
|
}
|
|
Free(dvd_buf);
|
|
}
|
|
|
|
Bool ATARBlks(CDrv *dv, U8 *buf, I64 blk, I64 cnt) {
|
|
I64 n;
|
|
CBlkDev *bd = dv->bd;
|
|
while (cnt > 0) {
|
|
n = cnt;
|
|
if (n > bd->max_reads)
|
|
n = bd->max_reads;
|
|
if (bd->type == BDT_ATAPI)
|
|
ATAPIReadBlks(bd, buf, blk, n);
|
|
else
|
|
ATAReadBlks(bd, buf, blk, n);
|
|
buf += n << BLK_SIZE_BITS;
|
|
blk += n;
|
|
cnt -= n;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
U0 ATAWriteBlks(CBlkDev *bd, U8 *buf, I64 blk,
|
|
I64 cnt) { // For low level disk access.
|
|
// Use BlkWrite() instead.
|
|
I64 i, U32s_avail, sects_avail, retries = 3;
|
|
F64 timeout;
|
|
Bool unlock = BlkDevLock(bd);
|
|
retry:
|
|
ATABlkSel(bd, blk, cnt);
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
ATACmd(bd, ATA_WRITE_MULTI_EXT);
|
|
else
|
|
ATACmd(bd, ATA_WRITE_MULTI);
|
|
bd->flags |= BDF_LAST_WAS_WRITE;
|
|
while (cnt > 0) {
|
|
timeout = tS + 1.0;
|
|
while (TRUE) {
|
|
i = InU8(bd->base0 + ATAR0_STAT);
|
|
if (!(i & ATAS_DRDY) || !(i & ATAS_DRQ)) {
|
|
Yield;
|
|
} else
|
|
break;
|
|
if (/* i&ATAS_ERR||*/ tS > timeout) {
|
|
if (retries--) {
|
|
ATAWaitNotBUSY(bd, 0);
|
|
goto retry;
|
|
} else
|
|
throw('BlkDev');
|
|
}
|
|
}
|
|
sects_avail = 1;
|
|
U32s_avail = sects_avail << BLK_SIZE_BITS >> 2;
|
|
RepOutU32(buf, U32s_avail, bd->base0 + ATAR0_DATA);
|
|
buf += U32s_avail << 2;
|
|
cnt -= sects_avail;
|
|
retries = 3;
|
|
}
|
|
ATAWaitNotBUSY(bd, 0);
|
|
if (unlock)
|
|
BlkDevUnlock(bd);
|
|
}
|
|
|
|
Bool ATAPISync(CBlkDev *bd) {
|
|
Bool okay = TRUE;
|
|
if (!ATAWaitNotBUSY(bd, 0))
|
|
okay = FALSE;
|
|
else {
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, 0);
|
|
OutU8(bd->base0 + ATAR0_HCYL, 0);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0x3500, 0, 0, 0, 0, 0);
|
|
if (!ATAWaitNotBUSY(bd, 0))
|
|
okay = FALSE;
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
U0 ATAPIClose(CBlkDev *bd, I64 close_field = 0x200,
|
|
I64 track = 0) { // 0x200 CD/DVD part 1
|
|
// 0x300 DVD part 2
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, 0);
|
|
OutU8(bd->base0 + ATAR0_HCYL, 0);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0x5B00, close_field, track, 0, 0, 0);
|
|
ATAWaitNotBUSY(bd, 0);
|
|
}
|
|
|
|
U0 ATAPIWriteBlks(CBlkDev *bd, U8 *buf, I64 native_blk, I64 cnt) {
|
|
I64 U32s_avail;
|
|
U8 *buf2;
|
|
ATAWaitNotBUSY(bd, 0);
|
|
ATAPISeek(bd, native_blk);
|
|
|
|
OutU8(bd->base0 + ATAR0_FEAT, 0);
|
|
OutU8(bd->base0 + ATAR0_LCYL, bd->blk_size);
|
|
OutU8(bd->base0 + ATAR0_HCYL, bd->blk_size.u8[1]);
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_CMD, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0x0400, native_blk.u16[1], native_blk, cnt.u16[1],
|
|
cnt, 0);
|
|
bd->flags |= BDF_LAST_WAS_WRITE;
|
|
ATAWaitNotBUSY(bd, 0);
|
|
|
|
ATAPISeek(bd, native_blk);
|
|
|
|
if (bd->flags & BDF_EXT_SIZE)
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xEF | bd->unit << 4);
|
|
else
|
|
OutU8(bd->base0 + ATAR0_SEL, 0xE0 | bd->unit << 4);
|
|
OutU8(bd->base0 + ATAR0_LCYL, bd->blk_size);
|
|
OutU8(bd->base0 + ATAR0_HCYL, bd->blk_size.u8[1]);
|
|
ATACmd(bd, ATA_PACKET);
|
|
ATAPIWritePktWord(bd, 0, 0xAA00, native_blk.u16[1], native_blk, cnt.u16[1],
|
|
cnt, 0);
|
|
buf2 = buf + bd->blk_size * cnt;
|
|
while (buf < buf2) {
|
|
ATAWaitDRQ(bd, 0);
|
|
U32s_avail =
|
|
(InU8(bd->base0 + ATAR0_HCYL) << 8 + InU8(bd->base0 + ATAR0_LCYL)) >> 2;
|
|
if (buf + U32s_avail << 2 > buf2)
|
|
U32s_avail = (buf2 - buf) >> 2;
|
|
if (U32s_avail) {
|
|
RepOutU32(buf, U32s_avail, bd->base0 + ATAR0_DATA);
|
|
buf += U32s_avail << 2;
|
|
blkdev.write_cnt += U32s_avail >> (BLK_SIZE_BITS - 2);
|
|
}
|
|
}
|
|
ATAWaitNotBUSY(bd, 0);
|
|
}
|
|
|
|
Bool ATAWBlks(CDrv *dv, U8 *buf, I64 blk, I64 cnt) {
|
|
I64 n, spc;
|
|
CBlkDev *bd = dv->bd;
|
|
Bool unlock;
|
|
spc = bd->blk_size >> BLK_SIZE_BITS;
|
|
if (bd->type == BDT_ATAPI) {
|
|
unlock = BlkDevLock(bd);
|
|
ATAPIWaitReady(bd, 0);
|
|
}
|
|
while (cnt > 0) {
|
|
n = cnt;
|
|
if (n > bd->max_writes)
|
|
n = bd->max_writes;
|
|
if (bd->type == BDT_ATAPI)
|
|
ATAPIWriteBlks(bd, buf, blk / spc, (n + spc - 1) / spc);
|
|
else
|
|
ATAWriteBlks(bd, buf, blk, n);
|
|
buf += n << BLK_SIZE_BITS;
|
|
blk += n;
|
|
cnt -= n;
|
|
blkdev.write_cnt += n;
|
|
}
|
|
if (bd->type == BDT_ATAPI) {
|
|
ATAPISync(bd);
|
|
// ATAPIStartStop(bd,0,FALSE);
|
|
if (unlock)
|
|
BlkDevUnlock(bd);
|
|
}
|
|
return TRUE;
|
|
}
|