1 /*
2 * Copyright (c) 2012 Corey Tabaka
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include <dev/driver.h>
10 #include <dev/class/uart.h>
11 #include <lk/debug.h>
12 #include <assert.h>
13 #include <malloc.h>
14 #include <lk/err.h>
15 #include <lib/cbuf.h>
16 #include <platform/uart.h>
17 #include <arch/x86.h>
18 #include <kernel/thread.h>
19 #include <platform/interrupts.h>
20
21 struct device_class uart_device_class = {
22 .name = "uart",
23 };
24
25 struct uart_driver_state {
26 struct cbuf rx_buf;
27 struct cbuf tx_buf;
28 };
29
30 static status_t uart_init(struct device *dev);
31
32 static enum handler_return uart_irq_handler(void *arg);
33 static int uart_write_thread(void *arg);
34
35 static ssize_t uart_read(struct device *dev, void *buf, size_t len);
36 static ssize_t uart_write(struct device *dev, const void *buf, size_t len);
37
38 static struct uart_ops the_ops = {
39 .std = {
40 .device_class = &uart_device_class,
41 .init = uart_init,
42 },
43 .read = uart_read,
44 .write = uart_write,
45 };
46
47 DRIVER_EXPORT(uart, &the_ops.std);
48
uart_init(struct device * dev)49 static status_t uart_init(struct device *dev) {
50 status_t res = NO_ERROR;
51
52 if (!dev)
53 return ERR_INVALID_ARGS;
54
55 if (!dev->config)
56 return ERR_NOT_CONFIGURED;
57
58 const struct platform_uart_config *config = dev->config;
59
60 struct uart_driver_state *state = malloc(sizeof(struct uart_driver_state));
61 if (!state) {
62 res = ERR_NO_MEMORY;
63 goto done;
64 }
65
66 dev->state = state;
67
68 /* set up the driver state */
69 cbuf_initialize(&state->rx_buf, config->rx_buf_len);
70 cbuf_initialize(&state->tx_buf, config->tx_buf_len);
71
72 /* configure the uart */
73 int divisor = 115200 / config->baud_rate;
74
75 outp(config->io_port + 3, 0x80); // set up to load divisor latch
76 outp(config->io_port + 0, divisor & 0xff); // lsb
77 outp(config->io_port + 1, divisor >> 8); // msb
78 outp(config->io_port + 3, 3); // 8N1
79 outp(config->io_port + 2, 0x07); // enable FIFO, clear, 14-byte threshold
80
81 register_int_handler(config->irq, uart_irq_handler, dev);
82 unmask_interrupt(config->irq);
83
84 //outp(config->io_port + 1, 0x3); // enable rx data available and tx holding empty interrupts
85 outp(config->io_port + 1, 0x1); // enable rx data available interrupts
86
87 thread_resume(thread_create("[uart writer]", uart_write_thread, dev, DEFAULT_PRIORITY,
88 DEFAULT_STACK_SIZE));
89
90 done:
91 return res;
92 }
93
uart_irq_handler(void * arg)94 static enum handler_return uart_irq_handler(void *arg) {
95 bool resched = false;
96 struct device *dev = arg;
97
98 DEBUG_ASSERT(dev);
99 DEBUG_ASSERT(dev->config);
100 DEBUG_ASSERT(dev->state);
101
102 const struct platform_uart_config *config = dev->config;
103 struct uart_driver_state *state = dev->state;
104
105 while (inp(config->io_port + 5) & (1<<0)) {
106 char c = inp(config->io_port + 0);
107 cbuf_write(&state->rx_buf, &c, 1, false);
108 resched = true;
109 }
110
111 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
112 }
113
uart_write_thread(void * arg)114 static int uart_write_thread(void *arg) {
115 struct device *dev = arg;
116
117 DEBUG_ASSERT(dev);
118 DEBUG_ASSERT(dev->config);
119 DEBUG_ASSERT(dev->state);
120
121 const struct platform_uart_config *config = dev->config;
122 struct uart_driver_state *state = dev->state;
123
124 return 0;
125
126 while (true) {
127 char c = cbuf_read(&state->tx_buf, &c, 1, true);
128
129 while ((inp(config->io_port + 5) & (1<<6)) == 0)
130 ;
131
132 outp(config->io_port + 0, c);
133 }
134
135 return 0;
136 }
137
uart_read(struct device * dev,void * buf,size_t len)138 static ssize_t uart_read(struct device *dev, void *buf, size_t len) {
139 if (!dev || !buf)
140 return ERR_INVALID_ARGS;
141
142 DEBUG_ASSERT(dev->state);
143 struct uart_driver_state *state = dev->state;
144
145 return cbuf_read(&state->rx_buf, buf, len, true);
146 }
147
uart_write(struct device * dev,const void * buf,size_t len)148 static ssize_t uart_write(struct device *dev, const void *buf, size_t len) {
149 if (!dev || !buf)
150 return ERR_INVALID_ARGS;
151
152 DEBUG_ASSERT(dev->state);
153 struct uart_driver_state *state = dev->state;
154
155 return cbuf_write(&state->tx_buf, buf, len, true);
156 }
157
158