1#include <acpi/ioapic.h>
2#include <asm.h>
3#include <block/bcache.h>
4#include <block/block.h>
5#include <block/sched.h>
6#include <compiler.h>
7#include <console/printf.h>
8#include <drivers/ata.h>
9#include <irq/idt.h>
10#include <mem/alloc.h>
11#include <mem/alloc_or_die.h>
12#include <stddef.h>
13#include <stdint.h>
14#include <time/spin_sleep.h>
15
16LOG_SITE_DECLARE_DEFAULT(ide);
17LOG_HANDLE_DECLARE_DEFAULT(ide);
18
19void ide_print_info(struct block_device *d) {
20 struct ata_drive *drive = (struct ata_drive *) d->driver_data;
21 if (!drive->actually_exists)
22 return;
23}
24
25static void swap_str(char *dst, const uint16_t *src, uint64_t word_len) {
26 for (uint64_t i = 0; i < word_len; i++) {
27 dst[2 * i] = (src[i] >> 8) & 0xFF;
28 dst[2 * i + 1] = src[i] & 0xFF;
29 }
30 dst[2 * word_len] = '\0';
31
32 for (int i = 2 * word_len - 1; i >= 0; --i) {
33 if (dst[i] == ' ' || dst[i] == '\0')
34 dst[i] = '\0';
35 else
36 break;
37 }
38}
39
40void ide_identify(struct ata_drive *drive) {
41 uint16_t *buf = kmalloc_or_die(256 * sizeof(uint16_t));
42
43 uint16_t io = drive->io_base;
44
45 outb(REG_DRIVE_HEAD(io), value: 0xA0 | (drive->slave ? 0x10 : 0x00));
46
47 outb(REG_COMMAND(io), ATA_CMD_IDENTIFY);
48
49 uint8_t status;
50
51 uint64_t timeout = IDE_IDENT_TIMEOUT_MS * 1000;
52 while ((status = inb(REG_STATUS(io))) & STATUS_BSY) {
53 sleep_spin_us(us: 10);
54 timeout--;
55 if (timeout == 0)
56 goto out;
57 }
58
59 if (status == 0 || (status & STATUS_ERR)) {
60 goto out;
61 }
62
63 timeout = IDE_IDENT_TIMEOUT_MS * 1000;
64 while (!((status = inb(REG_STATUS(io))) & STATUS_DRQ)) {
65 sleep_spin_us(us: 10);
66 timeout--;
67 if (timeout == 0)
68 goto out;
69 }
70
71 insw(REG_DATA(io), addr: buf, count: 256);
72
73 swap_str(dst: drive->serial, src: &buf[10], word_len: 10);
74 swap_str(dst: drive->firmware, src: &buf[23], word_len: 4);
75 swap_str(dst: drive->model, src: &buf[27], word_len: 20);
76
77 for (int i = 39; i >= 0 && drive->model[i] == ' '; i--)
78 drive->model[i] = '\0';
79
80 drive->supports_lba48 = (buf[83] & (1 << 10)) ? 1 : 0;
81
82 if (drive->supports_lba48) {
83 drive->total_sectors =
84 ((uint64_t) buf[100]) | ((uint64_t) buf[101] << 16) |
85 ((uint64_t) buf[102] << 32) | ((uint64_t) buf[103] << 48);
86 } else {
87 drive->total_sectors =
88 ((uint32_t) buf[60]) | ((uint32_t) buf[61] << 16);
89 }
90
91 drive->actually_exists = drive->total_sectors != 0;
92
93 drive->supports_dma = (buf[49] & (1 << 8)) ? 1 : 0;
94
95 drive->udma_mode = 0;
96 if (buf[88] & (1 << 13)) {
97 for (int i = 0; i < 8; i++) {
98 if (buf[88] & (1 << i)) {
99 drive->udma_mode = i;
100 }
101 }
102 }
103
104 drive->pio_mode = buf[64] & 0x03;
105out:
106 kfree(buf);
107}
108
109static struct bio_scheduler_ops ide_bio_ops = {
110 .should_coalesce = noop_should_coalesce,
111 /* .reorder = ide_reorder, */
112 .do_coalesce = noop_do_coalesce,
113
114 .max_wait_time =
115 {
116 [BIO_RQ_BACKGROUND] = 35,
117 [BIO_RQ_LOW] = 25,
118 [BIO_RQ_MEDIUM] = 10,
119 [BIO_RQ_HIGH] = 4,
120 [BIO_RQ_URGENT] = 0,
121 },
122
123 .dispatch_threshold = 96,
124
125 .boost_occupance_limit =
126 {
127 [BIO_RQ_BACKGROUND] = 64,
128 [BIO_RQ_LOW] = 56,
129 [BIO_RQ_MEDIUM] = 48,
130 [BIO_RQ_HIGH] = 40,
131 [BIO_RQ_URGENT] = 32,
132 },
133 .min_wait_ms = 1,
134 .tick_ms = 25,
135};
136
137struct block_device *ide_create_generic(struct ata_drive *ide) {
138 ide_identify(drive: ide);
139 if (!ide->actually_exists)
140 return NULL;
141
142 irq_t irq = irq_alloc_entry();
143 ide_log(LOG_INFO, "IDE drive IRQ on line %u, allocated entry %u", ide->irq,
144 irq);
145
146 ioapic_route_irq(irq: ide->irq, vector: irq, dest_apic_id: 0, false);
147 irq_register(name: "ata", vector: irq, handler: ide_irq_handler, ctx: &ide->channel, flags: IRQ_FLAG_NONE);
148 irq_set_chip(vector: irq, chip: lapic_get_chip(), NULL);
149 ide->channel.current_drive = ide;
150
151 struct block_device *d = kmalloc_or_die(sizeof(struct block_device));
152
153 d->driver_data = ide;
154 d->sector_size = ide->sector_size;
155 d->read_sector = ide_read_sector_wrapper;
156 d->write_sector = ide_write_sector_wrapper;
157 d->submit_bio_async = ide_submit_bio_async;
158
159 d->flags = BDEV_FLAG_NO_COALESCE | BDEV_FLAG_NO_REORDER;
160
161 d->cache = kmalloc(sizeof(struct bcache), ALLOC_FLAGS_ZERO);
162 if (!d->cache)
163 panic("Could not allocate space for IDE drive block cache");
164
165 d->scheduler = bio_sched_create(disk: d, ops: &ide_bio_ops);
166
167 bcache_init(cache: d->cache, DEFAULT_BLOCK_CACHE_SIZE);
168
169 d->type = BDEV_IDE_DRIVE;
170 return d;
171}
172