| 1 | #include <asm.h> |
| 2 | #include <block/bcache.h> |
| 3 | #include <block/block.h> |
| 4 | #include <console/printf.h> |
| 5 | #include <drivers/ata.h> |
| 6 | #include <mem/alloc.h> |
| 7 | #include <stdbool.h> |
| 8 | #include <stdint.h> |
| 9 | #include <time/spin_sleep.h> |
| 10 | |
| 11 | #define ATAPI_SECTOR_SIZE 2048 |
| 12 | |
| 13 | bool atapi_identify(struct ata_drive *ide) { |
| 14 | ata_select_drive(ata_drive: ide); |
| 15 | io_wait(); |
| 16 | |
| 17 | outb(REG_COMMAND(ide->io_base), value: 0xA1); |
| 18 | uint8_t status = inb(REG_STATUS(ide->io_base)); |
| 19 | |
| 20 | if (status == 0) |
| 21 | return false; |
| 22 | |
| 23 | uint64_t timeout = ATAPI_CMD_TIMEOUT_MS * 1000; |
| 24 | while ((status & STATUS_BSY)) { |
| 25 | status = inb(REG_STATUS(ide->io_base)); |
| 26 | sleep_spin_us(us: 10); |
| 27 | timeout--; |
| 28 | if (timeout == 0) |
| 29 | return false; |
| 30 | } |
| 31 | |
| 32 | if (!(inb(REG_LBA_MID(ide->io_base)) == 0x14 && |
| 33 | inb(REG_LBA_HIGH(ide->io_base)) == 0xEB)) |
| 34 | return false; |
| 35 | |
| 36 | insw(port: ide->io_base, addr: (uint16_t *) ide->identify_data, count: 256); |
| 37 | return true; |
| 38 | } |
| 39 | |
| 40 | bool atapi_read_sector(struct block_device *disk, uint64_t lba, uint8_t *buffer, |
| 41 | uint64_t sector_count) { |
| 42 | if (sector_count != 1) |
| 43 | return false; |
| 44 | |
| 45 | struct ata_drive *atapi = (struct ata_drive *) disk->driver_data; |
| 46 | uint16_t io = atapi->io_base; |
| 47 | |
| 48 | outb(REG_DRIVE_HEAD(io), value: atapi->slave ? 0xB0 : 0xA0); |
| 49 | io_wait(); |
| 50 | |
| 51 | outb(REG_FEATURES(io), value: 0); |
| 52 | outb(REG_LBA_MID(io), ATAPI_SECTOR_SIZE & 0xFF); |
| 53 | outb(REG_LBA_HIGH(io), ATAPI_SECTOR_SIZE >> 8); |
| 54 | |
| 55 | outb(REG_COMMAND(io), ATA_CMD_PACKET); |
| 56 | |
| 57 | uint64_t timeout = ATAPI_CMD_TIMEOUT_MS * 1000; |
| 58 | while (inb(REG_STATUS(io)) & STATUS_BSY) { |
| 59 | sleep_spin_us(us: 10); |
| 60 | timeout--; |
| 61 | if (timeout == 0) |
| 62 | return false; |
| 63 | } |
| 64 | |
| 65 | if (!(inb(REG_STATUS(io)) & STATUS_DRQ)) |
| 66 | return false; |
| 67 | |
| 68 | uint8_t packet[12] = {0x28, // READ (10) |
| 69 | 0, |
| 70 | (lba >> 24) & 0xFF, |
| 71 | (lba >> 16) & 0xFF, |
| 72 | (lba >> 8) & 0xFF, |
| 73 | (lba >> 0) & 0xFF, |
| 74 | 0, |
| 75 | 0, |
| 76 | 1, // 1 sector |
| 77 | 0, |
| 78 | 0, |
| 79 | 0}; |
| 80 | |
| 81 | for (int i = 0; i < 6; i++) { |
| 82 | uint16_t word = ((uint16_t *) packet)[i]; |
| 83 | outw(REG_DATA(io), value: word); |
| 84 | } |
| 85 | |
| 86 | timeout = ATAPI_CMD_TIMEOUT_MS * 1000; |
| 87 | while (true) { |
| 88 | uint8_t status = inb(REG_STATUS(io)); |
| 89 | if (status & STATUS_ERR) |
| 90 | return false; |
| 91 | if (status & STATUS_DRQ) |
| 92 | break; |
| 93 | sleep_spin_us(us: 10); |
| 94 | timeout--; |
| 95 | if (timeout == 0) |
| 96 | return false; |
| 97 | } |
| 98 | |
| 99 | for (int i = 0; i < ATAPI_SECTOR_SIZE / 2; i++) { |
| 100 | uint16_t word = inw(REG_DATA(io)); |
| 101 | buffer[i * 2] = word & 0xFF; |
| 102 | buffer[i * 2 + 1] = (word >> 8) & 0xFF; |
| 103 | } |
| 104 | |
| 105 | for (int i = 0; i < 4; i++) |
| 106 | inb(REG_STATUS(io)); |
| 107 | |
| 108 | return true; |
| 109 | } |
| 110 | |
| 111 | bool atapi_write_sector(struct block_device *disk, uint64_t lba, |
| 112 | const uint8_t *buffer, uint64_t sector_count) { |
| 113 | (void) disk; |
| 114 | (void) lba; |
| 115 | (void) buffer; |
| 116 | (void) sector_count; |
| 117 | return false; // no can write to cd |
| 118 | // TODO: newer CDs support write :boom: |
| 119 | } |
| 120 | |
| 121 | bool atapi_read_sector_wrapper(struct block_device *disk, uint64_t start_lba, |
| 122 | uint8_t *buffer, uint64_t sector_count) { |
| 123 | uint8_t *buf_ptr = buffer; |
| 124 | for (uint64_t i = 0; i < sector_count; i++) { |
| 125 | if (!atapi_read_sector(disk, lba: start_lba + i, buffer: buf_ptr, sector_count: 1)) { |
| 126 | return false; |
| 127 | } |
| 128 | buf_ptr += ATAPI_SECTOR_SIZE; |
| 129 | } |
| 130 | return true; |
| 131 | } |
| 132 | |
| 133 | void atapi_print_info(struct block_device *disk) { |
| 134 | struct ata_drive *d = disk->driver_data; |
| 135 | ata_ident_print(id: d->identify_data); |
| 136 | } |
| 137 | |
| 138 | struct block_device *atapi_create_generic(struct ata_drive *d) { |
| 139 | struct block_device *ret = kmalloc(sizeof(struct block_device)); |
| 140 | |
| 141 | if (!ret) |
| 142 | panic("Could not allocate space for ATAPI device" ); |
| 143 | |
| 144 | ret->driver_data = d; |
| 145 | ret->sector_size = 2048; |
| 146 | ret->read_sector = atapi_read_sector_wrapper; |
| 147 | ret->write_sector = atapi_write_sector; |
| 148 | ret->type = BDEV_ATAPI_DRIVE; |
| 149 | ret->cache = kmalloc(sizeof(struct bcache)); |
| 150 | if (!ret->cache) |
| 151 | panic("Could not allocate space for ATAPI device block cache" ); |
| 152 | |
| 153 | bcache_init(cache: ret->cache, DEFAULT_BLOCK_CACHE_SIZE); |
| 154 | return ret; |
| 155 | } |
| 156 | |