blob: be94bf60b5778d0624d48d76d9079f20da4106cb [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2012 Corey Tabaka
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <dev/driver.h>
25#include <dev/class/uart.h>
26#include <debug.h>
27#include <assert.h>
28#include <malloc.h>
29#include <err.h>
30#include <lib/cbuf.h>
31#include <platform/uart.h>
32#include <arch/x86.h>
33#include <kernel/thread.h>
34#include <platform/interrupts.h>
35
36struct device_class uart_device_class = {
37 .name = "uart",
38};
39
40struct uart_driver_state {
41 struct cbuf rx_buf;
42 struct cbuf tx_buf;
43};
44
45static status_t uart_init(struct device *dev);
46
47static enum handler_return uart_irq_handler(void *arg);
48static int uart_write_thread(void *arg);
49
50static ssize_t uart_read(struct device *dev, void *buf, size_t len);
51static ssize_t uart_write(struct device *dev, const void *buf, size_t len);
52
53static struct uart_ops the_ops = {
54 .std = {
55 .device_class = &uart_device_class,
56 .init = uart_init,
57 },
58 .read = uart_read,
59 .write = uart_write,
60};
61
62DRIVER_EXPORT(uart, &the_ops.std);
63
64static status_t uart_init(struct device *dev)
65{
66 status_t res = NO_ERROR;
67
68 if (!dev)
69 return ERR_INVALID_ARGS;
70
71 if (!dev->config)
72 return ERR_NOT_CONFIGURED;
73
74 const struct platform_uart_config *config = dev->config;
75
76 struct uart_driver_state *state = malloc(sizeof(struct uart_driver_state));
77 if (!state) {
78 res = ERR_NO_MEMORY;
79 goto done;
80 }
81
82 dev->state = state;
83
84 /* set up the driver state */
85 cbuf_initialize(&state->rx_buf, config->rx_buf_len);
86 cbuf_initialize(&state->tx_buf, config->tx_buf_len);
87
88 /* configure the uart */
89 int divisor = 115200 / config->baud_rate;
90
91 outp(config->io_port + 3, 0x80); // set up to load divisor latch
92 outp(config->io_port + 0, divisor & 0xff); // lsb
93 outp(config->io_port + 1, divisor >> 8); // msb
94 outp(config->io_port + 3, 3); // 8N1
95 outp(config->io_port + 2, 0x07); // enable FIFO, clear, 14-byte threshold
96
97 register_int_handler(config->irq, uart_irq_handler, dev);
98 unmask_interrupt(config->irq);
99
100 //outp(config->io_port + 1, 0x3); // enable rx data available and tx holding empty interrupts
101 outp(config->io_port + 1, 0x1); // enable rx data available interrupts
102
103 thread_resume(thread_create("[uart writer]", uart_write_thread, dev, DEFAULT_PRIORITY,
104 DEFAULT_STACK_SIZE));
105
106done:
107 return res;
108}
109
110static enum handler_return uart_irq_handler(void *arg)
111{
112 bool resched = false;
113 struct device *dev = arg;
114
115 DEBUG_ASSERT(dev);
116 DEBUG_ASSERT(dev->config);
117 DEBUG_ASSERT(dev->state);
118
119 const struct platform_uart_config *config = dev->config;
120 struct uart_driver_state *state = dev->state;
121
122 while (inp(config->io_port + 5) & (1<<0)) {
123 char c = inp(config->io_port + 0);
124 cbuf_write(&state->rx_buf, &c, 1, false);
125 resched = true;
126 }
127
128 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
129}
130
131static int uart_write_thread(void *arg)
132{
133 struct device *dev = arg;
134
135 DEBUG_ASSERT(dev);
136 DEBUG_ASSERT(dev->config);
137 DEBUG_ASSERT(dev->state);
138
139 const struct platform_uart_config *config = dev->config;
140 struct uart_driver_state *state = dev->state;
141
142 return 0;
143
144 while (true) {
145 char c = cbuf_read(&state->tx_buf, &c, 1, true);
146
147 while ((inp(config->io_port + 5) & (1<<6)) == 0)
148 ;
149
150 outp(config->io_port + 0, c);
151 }
152
153 return 0;
154}
155
156static ssize_t uart_read(struct device *dev, void *buf, size_t len)
157{
158 if (!dev || !buf)
159 return ERR_INVALID_ARGS;
160
161 DEBUG_ASSERT(dev->state);
162 struct uart_driver_state *state = dev->state;
163
164 return cbuf_read(&state->rx_buf, buf, len, true);
165}
166
167static ssize_t uart_write(struct device *dev, const void *buf, size_t len)
168{
169 if (!dev || !buf)
170 return ERR_INVALID_ARGS;
171
172 DEBUG_ASSERT(dev->state);
173 struct uart_driver_state *state = dev->state;
174
175 return cbuf_write(&state->tx_buf, buf, len, true);
176}
177