aboutsummaryrefslogtreecommitdiffstats
path: root/ata.hpp
blob: 5394c3127556f2d7391c979033ee1a65693c598b (plain)
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
126
127
128
129
130
131
#ifndef ATA_HPP
#define ATA_HPP

#include "portio.hpp"

#include <cstdint>
#include <utility>

namespace ATA
{
    enum class Type {
        None,
        ATA,
        ATAPI
    };

    enum class Base : std::uint16_t {
        Primary = 0x01F0,
        Secondary = 0x0170
    };

    enum class Drive : std::uint8_t {
        Master = 0xA0,
        Slave = 0xB0
    };

    enum class Command : std::uint8_t {
        Identify = 0xEC,
        IdentifyPacketDevice = 0xA1
    };

    namespace Status {
        static constexpr std::uint8_t Busy  = 0x80;
        static constexpr std::uint8_t Ready = 0x40;
        static constexpr std::uint8_t DF    = 0x20;
        static constexpr std::uint8_t DSC   = 0x10;
        static constexpr std::uint8_t DRQ   = 0x08;
        static constexpr std::uint8_t CORR  = 0x04;
        static constexpr std::uint8_t Index = 0x02;
        static constexpr std::uint8_t Error = 0x01;
    }

    static constexpr std::uint8_t ATAPIIdentify0 = 0x14;
    static constexpr std::uint8_t ATAPIIdentify1 = 0xEB;

    using enum Base;
    using enum Drive;

    template<Base PBase>
    struct Bus
    {
        template<Base b, unsigned offs>
        using BPort = Port<std::to_underlying(b) + offs>;

        [[no_unique_address]] BPort<PBase, 0> data;
        [[no_unique_address]] BPort<PBase, 1> errFeats;
        [[no_unique_address]] BPort<PBase, 2> count;
        [[no_unique_address]] BPort<PBase, 3> lba0;
        [[no_unique_address]] BPort<PBase, 4> lba1;
        [[no_unique_address]] BPort<PBase, 5> lba2;
        [[no_unique_address]] BPort<PBase, 6> select;
        [[no_unique_address]] BPort<PBase, 7> cmdStat;

        Type identify(Drive drv) {
            auto type = Type::None;

            data = std::to_underlying(drv);
            count = '\0';
            lba0 = '\0';
            lba1 = '\0';
            lba2 = '\0';
            cmdStat = std::to_underlying(Command::Identify);
    
            if (cmdStat == '\0')
                return type;

            type = Type::ATA;
            while (cmdStat & Status::Busy);

            std::uint8_t stat;
            do {
                stat = cmdStat;

                if (stat & Status::Error) {
                    if (lba1 == ATAPIIdentify0 && lba2 == ATAPIIdentify1) {
                        type = identifyAtapi(drv);
                        break;
                    } else {
                        return type;
                    }
                }
            } while (!(stat & Status::DRQ));

            if (type != Type::None) {
                for (int i = 0; i < 256; ++i) {
                    volatile std::uint16_t w = data;
                    (void)w;
                }
            }

            return type;
        }

        Type identifyAtapi(Drive drv) {
            data = std::to_underlying(drv);
            count = '\0';
            lba0 = '\0';
            lba1 = '\0';
            lba2 = '\0';
            cmdStat = std::to_underlying(Command::IdentifyPacketDevice);
    
            if (cmdStat == '\0')
                return Type::None;

            while (cmdStat & Status::Busy);

            std::uint8_t stat;
            do {
                stat = cmdStat;

                if (stat & Status::Error)
                    return Type::None;
            } while (!(stat & Status::DRQ));

            return Type::ATAPI;
        }
    };
} // ATA

#endif // ATA_HPP