1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
#include <kernel/arch/i386/ata.h>
#include <kernel/arch/i386/port_io.h>
#include <stdbool.h>
#include <kernel/arch/io.h>
enum {
LBAlo = 3,
LBAmid = 4,
LBAhi = 5,
DRV = 6,
CMD = 7,
STATUS = 7,
}; // offsets
// get I/O port base for drive
static uint16_t ata_iobase(int drive) {
bool secondary = drive&2;
return secondary ? 0x170 : 0x1F0;
}
static void ata_driveselect(int drive, int block) {
uint8_t v = 0xE0;
if (drive&1) // slave?
v |= 0x10; // set drive number bit
// TODO account for block
port_outb(ata_iobase(drive) + DRV, v);
}
static bool ata_identify(int drive) {
uint16_t iobase = ata_iobase(drive);
uint8_t v;
ata_driveselect(drive, 0);
for (int i = 2; i < 6; i++)
port_outb(iobase + i, 0);
port_outb(iobase + CMD, 0xEC); // IDENTIFY
v = port_inb(iobase + STATUS);
if (v == 0) return false; // nonexistent drive
while (port_inb(iobase + STATUS) & 0x80);
/* check for uncomformant devices, quit early */
if (port_inb(iobase + LBAmid) || port_inb(iobase + LBAhi)) {
// TODO atapi
return true;
}
/* pool until bit 3 (DRQ) or 0 (ERR) is set */
while (!((v = port_inb(iobase + STATUS) & 0x9)));
if (v & 1) return false; /* ERR was set, bail */
// now I can read 512 bytes of data, TODO
return true;
}
void ata_init(void) {
tty_const("\n");
for (int i = 0; i < 4; i++) {
tty_const("probing drive ");
_tty_var(i);
if (ata_identify(i))
tty_const(" - exists");
tty_const("\n");
}
}
|