U0 IDEATABlkSel(CBlkDev *bd, I64 blk, I64 count) { 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, count.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, count); 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, count); 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 IDEATAWaitNotBUSY(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 IDEATAWaitDRQ(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 IDEATANop(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 IDEATAWaitNotBUSY(bd, timeout); } U0 IDEATACmd(CBlkDev *bd, U8 cmd) { OutU8(bd->base0 + ATAR0_FEAT, 0); OutU8(bd->base0 + ATAR0_CMD, cmd); bd->last_time = tS; PortNop; } Bool IDEATAGetRes(CBlkDev *bd, F64 timeout, U8 *buf, I64 count, I64 _avail, Bool one_read) { I64 avail, overflow; bd->flags &= ~BDF_LAST_WAS_WRITE; MemSet(buf, 0, count); while (count > 0) { if (!IDEATAWaitDRQ(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 > count) { overflow = avail - count; avail = count; } else overflow = 0; if (avail & 2) RepInU16(buf, avail >> 1, bd->base0 + ATAR0_DATA); else RepInU32(buf, avail >> 2, bd->base0 + ATAR0_DATA); count -= 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 IDEATAWaitNotBUSY(bd, timeout); } Bool IDEATAPIWritePacketWord(CBlkDev *bd, F64 timeout, ...) { I64 i; for (i = 0; i < argc; i++) { if (!IDEATAWaitDRQ(bd, timeout)) return FALSE; OutU16(bd->base0 + ATAR0_DATA, EndianU16(argv[i])); bd->last_time = tS; } return TRUE; } Bool IDEATAPISetMaxSpeed(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); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_SET_CD_SPEED, 0xFFFF, 0xFFFF, 0, 0, 0); return IDEATAWaitNotBUSY(bd, 0); } Bool IDEATAPISeek(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); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_SEEK, native_blk >> 16, native_blk, 0, 0, 0); return IDEATAWaitNotBUSY(bd, 0); } Bool IDEATAPIStartStop(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); IDEATACmd(bd, ATA_PACKET); //Start/Stop if (IDEATAPIWritePacketWord(bd, timeout, ATAPI_START_STOP_UNIT, 0, i, 0, 0, 0)) return IDEATAWaitNotBUSY(bd, timeout); else return FALSE; } I64 IDEATAGetDevId(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); IDEATACmd(bd, ATA_IDENTIFY); if (IDEATAWaitNotBUSY(bd, timeout)) { if (InU8(bd->base0 + ATAR0_STAT) & ATAS_ERR) res = BDT_ATAPI; else { id_record = ZCAlloc(512); if (IDEATAGetRes(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 IDEATAReadNativeMax(CBlkDev *bd, F64 timeout) {//Returns zero on error 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); IDEATACmd(bd, ATA_DEV_RST); if (!IDEATAWaitNotBUSY(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 (IDEATAGetDevId(bd, timeout, TRUE) == BDT_NULL) okay = FALSE; else BEqual(&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); IDEATACmd(bd, ATA_READ_NATIVE_MAX_EXT); if (IDEATAWaitNotBUSY(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); IDEATACmd(bd, ATA_READ_NATIVE_MAX); if (IDEATAWaitNotBUSY(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 IDEATAPIReadCapacity(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 (IDEATAWaitNotBUSY(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); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0); if (!IDEATAGetRes(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 *IDEATAPIReadTrackInfo(CBlkDev *bd, I64 blk) { CATAPITrack *res = CAlloc(sizeof(CATAPITrack)); Bool unlock = BlkDevLock(bd); if (IDEATAWaitNotBUSY(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); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_READ_TRACK_INFO, blk.u16[1], blk.u16[0], (sizeof(CATAPITrack) & 0xFF00) >> 8, (sizeof(CATAPITrack) & 0x00FF) << 8, 0); if (!IDEATAGetRes(bd, 0, res, sizeof(CATAPITrack), 0, TRUE)) { Free(res); res = NULL; } } else { Free(res); res = NULL; } if (unlock) BlkDevUnlock(bd); return res; } I64 IDEATAProbe(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 IDEATAGetDevId(&bd, tS + 0.1, FALSE); } Bool IDEATAPISync(CBlkDev *bd) { Bool okay = TRUE; if (!IDEATAWaitNotBUSY(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); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_SYNC_CACHE, 0, 0, 0, 0, 0); if (!IDEATAWaitNotBUSY(bd, 0)) okay = FALSE; } return okay; } U0 IDEATAPIClose(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); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_CLOSE_TRACK_SESSION, close_field, track, 0, 0, 0); IDEATAWaitNotBUSY(bd, 0); } U0 IDEATAPIWriteBlks(CBlkDev *bd, U8 *buf, I64 native_blk, I64 count) { I64 U32s_avail; U8 *buf2; IDEATAWaitNotBUSY(bd, 0); IDEATAPISeek(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); IDEATAPIWritePacketWord(bd, 0, ATAPI_FORMAT_UNIT, native_blk.u16[1], native_blk, count.u16[1], count, 0); bd->flags |= BDF_LAST_WAS_WRITE; IDEATAWaitNotBUSY(bd, 0); IDEATAPISeek(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]); IDEATACmd(bd, ATA_PACKET); IDEATAPIWritePacketWord(bd, 0, ATAPI_WRITE, native_blk.u16[1], native_blk, count.u16[1], count, 0); buf2 = buf + bd->blk_size * count; while (buf < buf2) { IDEATAWaitDRQ(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_count += U32s_avail >> (BLK_SIZE_BITS - 2); } } IDEATAWaitNotBUSY(bd, 0); }