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_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
40bool 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
111bool 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
121bool 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
133void 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
138struct 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