aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/fs/ata_pio.c
blob: b6dcdcc8edbaf4aa99ce76490d7c941728cd0b9b (plain) (blame)
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <kernel/io/io.h>
#include <kernel/fs/ata_pio.h>
#include <kernel/memory/alloc.h>

struct ata_interface *new_ata(uint8_t master, uint16_t port_base)
{
    struct ata_interface *ret = (struct ata_interface *) kmalloc(sizeof(struct ata_interface));

    ret->master = master;
    ret->data_port = port_base;
    ret->error_port = (uint16_t) (port_base + 0x1);
    ret->sector_count_port = (uint16_t) (port_base + 0x2);
    ret->lba_low_port = (uint16_t) (port_base + 0x3);
    ret->lba_mid_port = (uint16_t) (port_base + 0x4);
    ret->lba_high_port = (uint16_t) (port_base + 0x5);
    ret->device_port = (uint16_t) (port_base + 0x6);
    ret->command_port = (uint16_t) (port_base + 0x7);
    ret->control_port = (uint16_t) (port_base + 0x206);

    return ret;
}

uint8_t ata_identify(struct ata_interface *interface, uint16_t *ret_data)
{
    outb(interface->device_port, (uint8_t) (interface->master ? 0xA0 : 0xB0));
    outb(interface->control_port, 0);

    outb(interface->device_port, 0xA0);
    uint8_t status = inb(interface->command_port);
    if (status == 0xFF) return 1;

    outb(interface->device_port, (uint8_t) (interface->master ? 0xA0 : 0xB0));
    outb(interface->sector_count_port, 0);
    outb(interface->lba_low_port, 0);
    outb(interface->lba_mid_port, 0);
    outb(interface->lba_high_port, 0);
    outb(interface->command_port, 0xEC); // Identify command

    status = inb(interface->command_port);
    if (!status) return 1;

    while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) {
        status = inb(interface->command_port);
    }

    if (status & 0x01) return 1;

    for (int i = 0; i < 256; i++) ret_data[i] = inw(interface->data_port);
    return 0;
}

uint8_t *ata_read28(struct ata_interface *interface, uint32_t sector)
{
    if (sector > 0x0FFFFFFF) return 0;

    outb(interface->device_port, (uint8_t) ((interface->master ? 0xE0 : 0xF0) | ((sector & 0x0F000000) >> 24)));

    uint8_t status = 0;
    for (int i = 0; i < 5; i++) status = inb(interface->command_port);
    if (status == 0xFF) return 0;

    outb(interface->error_port, 0);
    outb(interface->sector_count_port, 1);
    outb(interface->lba_low_port, (uint8_t) (sector & 0x000000FF));
    outb(interface->lba_mid_port, (uint8_t) ((sector & 0x0000FF00) >> 8));
    outb(interface->lba_high_port, (uint8_t) ((sector & 0x00FF0000) >> 16));
    outb(interface->command_port, 0x20); // Read command

    status = inb(interface->command_port);
    while ((status & 0x80) && !(status & 0x01)) status = inb(interface->command_port);

    uint8_t *ret = (uint8_t *) kmalloc(BYTES_PER_SECTOR);
    for (int i = 0; i < BYTES_PER_SECTOR; i += 2) {
        uint16_t data = inw(interface->data_port);
        ret[i] = (uint8_t) (data & 0xFF);
        ret[i + 1] = (uint8_t) ((data >> 8) & 0xFF);
    }
    return ret;
}

uint8_t ata_write28(struct ata_interface *interface, uint32_t sector, const uint8_t *contents)
{
    if (sector > 0x0FFFFFFF) return 1;
    asm ("cli");

    outb(interface->device_port, (uint8_t) ((interface->master ? 0xE0 : 0xF0) | ((sector & 0x0F000000) >> 24)));

    uint8_t status = 0;
    for (int i = 0; i < 5; i++) status = inb(interface->command_port);
    if (status == 0xFF) return 1;

    outb(interface->error_port, 0);
    outb(interface->sector_count_port, 1);
    outb(interface->lba_low_port, (uint8_t) (sector & 0x000000FF));
    outb(interface->lba_mid_port, (uint8_t) ((sector & 0x0000FF00) >> 8));
    outb(interface->lba_high_port, (uint8_t) ((sector & 0x00FF0000) >> 16));
    outb(interface->command_port, 0x30); // Write command

    while ((status & 0x80) || !(status & 0x08)) status = inb(interface->command_port);

    if (status & (0x01 || 0x20)) return 2;

    for (int i = 0; i < BYTES_PER_SECTOR; i += 2) {
        uint16_t data = contents[i];
        data |= ((uint16_t) contents[i + 1]) << 8;
        outw(interface->data_port, data);
    }

    outb(interface->command_port, 0xE7); // Flush command

    for (int i = 0; i < 5; i++) status = inb(interface->command_port);
    if (!status) return 3;

    while ((status & 0x80) && !(status & 0x01)) {
        status = inb(interface->command_port);
    }

    return 0;
}

uint8_t ata_clear28(struct ata_interface *interface, uint32_t sector)
{
    uint8_t empty_sector[512] = {0};
    return ata_write28(interface, sector, empty_sector);
}