Files
vt1200/reference/keyboard-mouse/vtbt/src/uart.c
T
SDD 3fd1e13b2e docs: keyboard/mouse protocol spec — LK401 (LK201 protocol) + VSXXX-AA mouse, sources, MAME lk201 device
Distils public docs into reference/keyboard-mouse/PROTOCOL.md for the level-1
Keyboard (27) / Mouse (26) device tests:
- keyboard: 4800 8N1 RS-423, self-test via FD -> 01 00 00 00 (errors 3D/3E)
- mouse: 4800 8O1 RS-232, self-test via 'T' -> 4-byte report (errors 3D/3E, >=0x20 fatal)
- separate ports: DUART2 A=keyboard, B=mouse (NOT daisy-chained)
MAME already has an LK201 device (src/mame/dec/lk201.cpp/.h) but no DEC mouse.
2026-10-16 11:38:52 +03:00

218 lines
3.8 KiB
C

#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/ring_buffer.h>
#include "uart.h"
LOG_MODULE_REGISTER(uart, CONFIG_LOG_DEFAULT_LEVEL);
#define UART_DEVICE_NODE DT_CHOSEN(zephyr_vt_uart)
/* 4-byte TX buffer, just like on the LK201. */
RING_BUF_DECLARE(tx_buf, 4);
/* Given by TX callback when new space is available in the TX buffer. */
K_SEM_DEFINE(tx_space_sem, 0, 1);
static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
/* Enable the RS-423 driver. It is disabled by default in order to prevent
* transmission of bootloader, etc. output from the devkit. */
static const struct gpio_dt_spec uart_tx_enable =
GPIO_DT_SPEC_GET_OR(DT_NODELABEL(uart_tx_enable), gpios, {0});
static serial_cb user_callback = NULL;
static atomic_t locked;
static bool overflow;
static void
callback_tx(void)
{
uint32_t size;
int filled_size;
uint8_t *data;
if (atomic_get(&locked)) {
uart_irq_tx_disable(uart_dev);
return;
}
while (!ring_buf_is_empty(&tx_buf)) {
size = ring_buf_get_claim(&tx_buf, &data, 4);
filled_size = uart_fifo_fill(uart_dev, data, size);
if (filled_size < 0) {
ring_buf_get_finish(&tx_buf, 0);
return;
}
int ret = ring_buf_get_finish(&tx_buf, (uint32_t)filled_size);
if (filled_size > 0) {
k_sem_give(&tx_space_sem);
}
if (ret < 0) {
return;
}
}
uart_irq_tx_disable(uart_dev);
}
static void
callback_rx(void)
{
uint8_t c;
/* read until FIFO empty */
while (uart_fifo_read(uart_dev, &c, 1) == 1) {
if (user_callback) {
user_callback(c);
}
}
}
static void
callback(const struct device *dev, void *user_data)
{
ARG_UNUSED(dev);
ARG_UNUSED(user_data);
if (uart_irq_update(uart_dev) < 0) {
return;
}
if (uart_irq_tx_ready(uart_dev) > 0) {
callback_tx();
}
if (uart_irq_rx_ready(uart_dev) > 0) {
callback_rx();
}
}
int
uart_init(void)
{
int ret;
if (!device_is_ready(uart_dev)) {
LOG_ERR("UART device not found");
return -1;
}
if (!gpio_is_ready_dt(&uart_tx_enable)) {
LOG_ERR("UART TX enable pin GPIO port is not ready.");
return -1;
}
ret = gpio_pin_configure_dt(&uart_tx_enable, GPIO_OUTPUT_ACTIVE);
if (ret != 0) {
LOG_ERR("Configuring GPIO pin failed: %d", ret);
return -1;
}
return 0;
}
int
uart_set_rx_callback(serial_cb serial_cb)
{
int ret;
user_callback = serial_cb;
ret = uart_irq_callback_user_data_set(uart_dev, callback, NULL);
if (ret < 0) {
if (ret == -ENOTSUP) {
LOG_ERR("Interrupt-driven UART not enabled");
} else if (ret == -ENOSYS) {
LOG_ERR("UART does not support interrupt-driven API");
} else {
LOG_ERR("Error setting UART callback: %d", ret);
}
return -1;
}
uart_irq_rx_enable(uart_dev);
return 0;
}
int
uart_write_byte(unsigned char out_char)
{
while (ring_buf_put(&tx_buf, &out_char, 1) < 1) {
if (atomic_get(&locked)) {
overflow = true;
return 0;
}
k_sem_take(&tx_space_sem, K_FOREVER);
}
uart_irq_tx_enable(uart_dev);
return 1;
}
int
uart_write(const unsigned char buf[], size_t count)
{
uint32_t total = 0;
while (true) {
uint32_t wrote = ring_buf_put(
&tx_buf, &buf[total], count - total);
if (atomic_get(&locked)) {
if (wrote < count) {
overflow = true;
}
return (int)wrote;
}
if (wrote > 0) {
uart_irq_tx_enable(uart_dev);
}
total += wrote;
if (total >= count) {
break;
}
k_sem_take(&tx_space_sem, K_FOREVER);
}
return count;
}
void
uart_flush(void)
{
uart_irq_tx_enable(uart_dev);
while (!ring_buf_is_empty(&tx_buf)) {
k_sem_take(&tx_space_sem, K_FOREVER);
}
}
void
uart_lock(void)
{
if (atomic_get(&locked)) {
return;
}
atomic_set(&locked, 1);
overflow = false;
}
void
uart_unlock(void)
{
if (!atomic_get(&locked)) {
return;
}
atomic_set(&locked, 0);
uart_flush();
}
bool
uart_overflow_get(void)
{
return overflow;
}