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
13bool atapi_identify(struct ata_drive *ide) {
14 ata_select_drive(ata_drive: ide);
15 io_port_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
40static bool atapi_read_sector(struct block_device *disk, uint64_t lba,
41 uint8_t *buffer, 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_port_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
111static bool atapi_write_sector(struct block_device *disk, uint64_t lba,
112 const uint8_t *buffer, uint64_t sector_count) {
113 cc_ignore(disk, lba, buffer, sector_count);
114 return false; // no can write to cd
115 // TODO: newer CDs support write :boom:
116}
117
118static bool atapi_read_sector_wrapper(struct block_device *disk,
119 uint64_t start_lba, uint8_t *buffer,
120 uint64_t sector_count) {
121 uint8_t *buf_ptr = buffer;
122 for (uint64_t i = 0; i < sector_count; i++) {
123 if (!atapi_read_sector(disk, lba: start_lba + i, buffer: buf_ptr, sector_count: 1)) {
124 return false;
125 }
126 buf_ptr += ATAPI_SECTOR_SIZE;
127 }
128 return true;
129}
130
131void atapi_print_info(struct block_device *disk) {
132 struct ata_drive *d = disk->driver_data;
133 ata_ident_print(id: d->identify_data);
134}
135
136struct block_device *atapi_create_generic(struct ata_drive *d) {
137 struct block_device *ret = vnew(ret);
138
139 if (!ret)
140 panic(OUT_OF_MEMORY, "ATAPI device");
141
142 ret->driver_data = d;
143 ret->sector_size = 2048;
144 ret->read_sector = atapi_read_sector_wrapper;
145 ret->write_sector = atapi_write_sector;
146 ret->type = BDEV_ATAPI_DRIVE;
147 ret->cache = vnew(ret->cache);
148 if (!ret->cache)
149 panic(OUT_OF_MEMORY, "ATAPI device block cache");
150
151 bcache_init(cache: ret->cache, DEFAULT_BLOCK_CACHE_SIZE);
152 return ret;
153}
154