bmik/Src/DskATA.HC

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;
}