git.cappig.dev / apheleiaOS.git master

1
#include <deque>
2
#include <errno.h>
3
#include <fcntl.h>
4
#include <riscv/mmio_plugin.h>
5
#include <stdint.h>
6
#include <stdio.h>
7
#include <string.h>
8
#include <string>
9
#include <sys/types.h>
10
#include <termios.h>
11
#include <unistd.h>
12
13
namespace {
14
15
constexpr reg_t UART_REG_THR_RBR_DLL = 0x00;
16
constexpr reg_t UART_REG_IER_DLM = 0x01;
17
constexpr reg_t UART_REG_IIR_FCR = 0x02;
18
constexpr reg_t UART_REG_LCR = 0x03;
19
constexpr reg_t UART_REG_MCR = 0x04;
20
constexpr reg_t UART_REG_LSR = 0x05;
21
constexpr reg_t UART_REG_MSR = 0x06;
22
constexpr reg_t UART_REG_SCR = 0x07;
23
24
constexpr uint8_t UART_LCR_DLAB = 0x80;
25
26
constexpr uint8_t UART_LSR_DR = 0x01;
27
constexpr uint8_t UART_LSR_THRE = 0x20;
28
constexpr uint8_t UART_LSR_TEMT = 0x40;
29
30
class ns16550a_plugin_t {
31
  public:
32
    explicit ns16550a_plugin_t(const std::string &args) : args_(args) {
33
        const bool use_tty = args_.find("tty") != std::string::npos;
34
        if (use_tty) {
35
            int tty_in = open("/dev/tty", O_RDONLY | O_CLOEXEC);
36
            int tty_out = open("/dev/tty", O_WRONLY | O_CLOEXEC);
37
38
            if (tty_in >= 0 && tty_out >= 0) {
39
                stdin_fd_ = tty_in;
40
                stdout_fd_ = tty_out;
41
                close_stdin_ = true;
42
                close_stdout_ = true;
43
            } else {
44
                if (tty_in >= 0) {
45
                    close(tty_in);
46
                }
47
                if (tty_out >= 0) {
48
                    close(tty_out);
49
                }
50
51
                stdin_fd_ = STDIN_FILENO;
52
                stdout_fd_ = STDOUT_FILENO;
53
            }
54
        } else {
55
            stdin_fd_ = STDIN_FILENO;
56
            stdout_fd_ = STDOUT_FILENO;
57
        }
58
59
        if (isatty(stdin_fd_) == 1 && tcgetattr(stdin_fd_, &stdin_termios_) == 0) {
60
            stdin_termios_saved_ = true;
61
62
            struct termios raw = stdin_termios_;
63
            raw.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
64
            raw.c_iflag &= ~(IXON | IXOFF | ICRNL | INLCR | IGNCR);
65
            raw.c_cc[VMIN] = 1;
66
            raw.c_cc[VTIME] = 0;
67
            (void)tcsetattr(stdin_fd_, TCSANOW, &raw);
68
        }
69
70
        stdin_flags_ = fcntl(stdin_fd_, F_GETFL, 0);
71
        if (stdin_flags_ >= 0) {
72
            (void)fcntl(stdin_fd_, F_SETFL, stdin_flags_ | O_NONBLOCK);
73
        }
74
    }
75
76
    ~ns16550a_plugin_t() {
77
        if (stdin_flags_ >= 0) {
78
            (void)fcntl(stdin_fd_, F_SETFL, stdin_flags_);
79
        }
80
81
        if (stdin_termios_saved_) {
82
            (void)tcsetattr(stdin_fd_, TCSANOW, &stdin_termios_);
83
        }
84
85
        if (close_stdin_) {
86
            close(stdin_fd_);
87
        }
88
        if (close_stdout_) {
89
            close(stdout_fd_);
90
        }
91
    }
92
93
    bool load(reg_t addr, size_t len, uint8_t *bytes) {
94
        if (!bytes || len == 0) {
95
            return false;
96
        }
97
98
        for (size_t i = 0; i < len; i++) {
99
            if (!load8(addr + i, &bytes[i])) {
100
                return false;
101
            }
102
        }
103
104
        return true;
105
    }
106
107
    bool store(reg_t addr, size_t len, const uint8_t *bytes) {
108
        if (!bytes || len == 0) {
109
            return false;
110
        }
111
112
        for (size_t i = 0; i < len; i++) {
113
            if (!store8(addr + i, bytes[i])) {
114
                return false;
115
            }
116
        }
117
118
        return true;
119
    }
120
121
  private:
122
    bool load8(reg_t addr, uint8_t *value) {
123
        if (!value) {
124
            return false;
125
        }
126
127
        pump_stdin();
128
129
        const reg_t reg = addr & 0x7;
130
        switch (reg) {
131
        case UART_REG_THR_RBR_DLL:
132
            if (lcr_ & UART_LCR_DLAB) {
133
                *value = dll_;
134
                return true;
135
            }
136
137
            if (rx_fifo_.empty()) {
138
                *value = 0;
139
                return true;
140
            }
141
142
            *value = rx_fifo_.front();
143
            rx_fifo_.pop_front();
144
            return true;
145
146
        case UART_REG_IER_DLM:
147
            *value = (lcr_ & UART_LCR_DLAB) ? dlm_ : ier_;
148
            return true;
149
150
        case UART_REG_IIR_FCR:
151
            *value = rx_fifo_.empty() ? 0x01 : 0x04;
152
            return true;
153
154
        case UART_REG_LCR:
155
            *value = lcr_;
156
            return true;
157
158
        case UART_REG_MCR:
159
            *value = mcr_;
160
            return true;
161
162
        case UART_REG_LSR: {
163
            uint8_t lsr = UART_LSR_THRE | UART_LSR_TEMT;
164
            if (!rx_fifo_.empty()) {
165
                lsr |= UART_LSR_DR;
166
            }
167
            *value = lsr;
168
            return true;
169
        }
170
171
        case UART_REG_MSR:
172
            *value = 0xB0;
173
            return true;
174
175
        case UART_REG_SCR:
176
            *value = scr_;
177
            return true;
178
179
        default:
180
            *value = 0;
181
            return true;
182
        }
183
    }
184
185
    bool store8(reg_t addr, uint8_t value) {
186
        const reg_t reg = addr & 0x7;
187
        switch (reg) {
188
        case UART_REG_THR_RBR_DLL:
189
            if (lcr_ & UART_LCR_DLAB) {
190
                dll_ = value;
191
                return true;
192
            }
193
194
            return write_stdout(value);
195
196
        case UART_REG_IER_DLM:
197
            if (lcr_ & UART_LCR_DLAB) {
198
                dlm_ = value;
199
            } else {
200
                ier_ = value;
201
            }
202
            return true;
203
204
        case UART_REG_IIR_FCR:
205
            return true;
206
207
        case UART_REG_LCR:
208
            lcr_ = value;
209
            return true;
210
211
        case UART_REG_MCR:
212
            mcr_ = value;
213
            return true;
214
215
        case UART_REG_SCR:
216
            scr_ = value;
217
            return true;
218
219
        default:
220
            return true;
221
        }
222
    }
223
224
    bool write_stdout(uint8_t ch) {
225
        ssize_t wrote = write(stdout_fd_, &ch, 1);
226
        if (wrote == 1) {
227
            return true;
228
        }
229
230
        if (wrote < 0 && (errno == EINTR || errno == EAGAIN)) {
231
            return true;
232
        }
233
234
        return false;
235
    }
236
237
    void pump_stdin() {
238
        if (stdin_fd_ < 0) {
239
            return;
240
        }
241
242
        uint8_t buf[256];
243
        while (true) {
244
            ssize_t n = read(stdin_fd_, buf, sizeof(buf));
245
            if (n > 0) {
246
                for (ssize_t i = 0; i < n; i++) {
247
                    rx_fifo_.push_back(buf[i]);
248
                }
249
                continue;
250
            }
251
252
            if (n == 0) {
253
                return;
254
            }
255
256
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
257
                return;
258
            }
259
260
            return;
261
        }
262
    }
263
264
    std::string args_;
265
    std::deque<uint8_t> rx_fifo_;
266
    int stdin_fd_ = -1;
267
    int stdout_fd_ = -1;
268
    int stdin_flags_ = -1;
269
    bool close_stdin_ = false;
270
    bool close_stdout_ = false;
271
    bool stdin_termios_saved_ = false;
272
    struct termios stdin_termios_ = {};
273
274
    uint8_t ier_ = 0;
275
    uint8_t lcr_ = 0;
276
    uint8_t mcr_ = 0;
277
    uint8_t scr_ = 0;
278
    uint8_t dll_ = 0;
279
    uint8_t dlm_ = 0;
280
};
281
282
mmio_plugin_registration_t<ns16550a_plugin_t> reg_ns16550a_plugin("ns16550a");
283
284
} // namespace