diff options
author | Corey Tabaka <corey.tabaka@gmail.com> | 2013-06-01 22:44:55 -0700 |
---|---|---|
committer | Corey Tabaka <corey.tabaka@gmail.com> | 2013-06-01 22:53:11 -0700 |
commit | e30cff8f915419e96069c2f4de1efff53c9dbf3b (patch) | |
tree | 0e6b72a7440c787e840e7ef5d1430847a97c9c7a | |
parent | e69493dd5a6d0abbf2b6d840465c51d1725a6198 (diff) | |
download | lk-e30cff8f915419e96069c2f4de1efff53c9dbf3b.tar.gz |
[platform][pc] Add UART driver.
-rw-r--r-- | platform/pc/include/platform/uart.h | 36 | ||||
-rw-r--r-- | platform/pc/rules.mk | 1 | ||||
-rw-r--r-- | platform/pc/uart.c | 177 |
3 files changed, 214 insertions, 0 deletions
diff --git a/platform/pc/include/platform/uart.h b/platform/pc/include/platform/uart.h new file mode 100644 index 00000000..78d6e3ac --- /dev/null +++ b/platform/pc/include/platform/uart.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012 Corey Tabaka + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __PLATFORM_UART_H +#define __PLATFORM_UART_H + +struct platform_uart_config { + int baud_rate; + int io_port; + int irq; + int rx_buf_len; + int tx_buf_len; +}; + +#endif + diff --git a/platform/pc/rules.mk b/platform/pc/rules.mk index c1a2199b..bbeb0fe8 100644 --- a/platform/pc/rules.mk +++ b/platform/pc/rules.mk @@ -20,6 +20,7 @@ MODULE_SRCS += \ $(LOCAL_DIR)/keyboard.c \ $(LOCAL_DIR)/pci.c \ $(LOCAL_DIR)/ide.c \ + $(LOCAL_DIR)/uart.c \ LINKER_SCRIPT += \ $(BUILDDIR)/kernel.ld diff --git a/platform/pc/uart.c b/platform/pc/uart.c new file mode 100644 index 00000000..268c9a45 --- /dev/null +++ b/platform/pc/uart.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2012 Corey Tabaka + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <dev/driver.h> +#include <dev/class/uart.h> +#include <debug.h> +#include <assert.h> +#include <malloc.h> +#include <err.h> +#include <lib/cbuf.h> +#include <platform/uart.h> +#include <arch/x86.h> +#include <kernel/thread.h> +#include <platform/interrupts.h> + +struct device_class uart_device_class = { + .name = "uart", +}; + +struct uart_driver_state { + struct cbuf rx_buf; + struct cbuf tx_buf; +}; + +static status_t uart_init(struct device *dev); + +static enum handler_return uart_irq_handler(void *arg); +static int uart_write_thread(void *arg); + +static ssize_t uart_read(struct device *dev, void *buf, size_t len); +static ssize_t uart_write(struct device *dev, const void *buf, size_t len); + +static struct uart_ops the_ops = { + .std = { + .device_class = &uart_device_class, + .init = uart_init, + }, + .read = uart_read, + .write = uart_write, +}; + +DRIVER_EXPORT(uart, &the_ops.std); + +static status_t uart_init(struct device *dev) +{ + status_t res = NO_ERROR; + + if (!dev) + return ERR_INVALID_ARGS; + + if (!dev->config) + return ERR_NOT_CONFIGURED; + + const struct platform_uart_config *config = dev->config; + + struct uart_driver_state *state = malloc(sizeof(struct uart_driver_state)); + if (!state) { + res = ERR_NO_MEMORY; + goto done; + } + + dev->state = state; + + /* set up the driver state */ + cbuf_initialize(&state->rx_buf, config->rx_buf_len); + cbuf_initialize(&state->tx_buf, config->tx_buf_len); + + /* configure the uart */ + int divisor = 115200 / config->baud_rate; + + outp(config->io_port + 3, 0x80); // set up to load divisor latch + outp(config->io_port + 0, divisor & 0xff); // lsb + outp(config->io_port + 1, divisor >> 8); // msb + outp(config->io_port + 3, 3); // 8N1 + outp(config->io_port + 2, 0x07); // enable FIFO, clear, 14-byte threshold + + register_int_handler(config->irq, uart_irq_handler, dev); + unmask_interrupt(config->irq); + + //outp(config->io_port + 1, 0x3); // enable rx data available and tx holding empty interrupts + outp(config->io_port + 1, 0x1); // enable rx data available interrupts + + thread_resume(thread_create("[uart writer]", uart_write_thread, dev, DEFAULT_PRIORITY, + DEFAULT_STACK_SIZE)); + +done: + return res; +} + +static enum handler_return uart_irq_handler(void *arg) +{ + bool resched = false; + struct device *dev = arg; + + DEBUG_ASSERT(dev); + DEBUG_ASSERT(dev->config); + DEBUG_ASSERT(dev->state); + + const struct platform_uart_config *config = dev->config; + struct uart_driver_state *state = dev->state; + + while (inp(config->io_port + 5) & (1<<0)) { + char c = inp(config->io_port + 0); + cbuf_write(&state->rx_buf, &c, 1, false); + resched = true; + } + + return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE; +} + +static int uart_write_thread(void *arg) +{ + struct device *dev = arg; + + DEBUG_ASSERT(dev); + DEBUG_ASSERT(dev->config); + DEBUG_ASSERT(dev->state); + + const struct platform_uart_config *config = dev->config; + struct uart_driver_state *state = dev->state; + + return 0; + + while (true) { + char c = cbuf_read(&state->tx_buf, &c, 1, true); + + while ((inp(config->io_port + 5) & (1<<6)) == 0) + ; + + outp(config->io_port + 0, c); + } + + return 0; +} + +static ssize_t uart_read(struct device *dev, void *buf, size_t len) +{ + if (!dev || !buf) + return ERR_INVALID_ARGS; + + DEBUG_ASSERT(dev->state); + struct uart_driver_state *state = dev->state; + + return cbuf_read(&state->rx_buf, buf, len, true); +} + +static ssize_t uart_write(struct device *dev, const void *buf, size_t len) +{ + if (!dev || !buf) + return ERR_INVALID_ARGS; + + DEBUG_ASSERT(dev->state); + struct uart_driver_state *state = dev->state; + + return cbuf_write(&state->tx_buf, buf, len, true); +} + |