Files
buzzer/firmware/src/usb.c
2026-02-25 12:28:28 +01:00

207 lines
5.3 KiB
C

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/usb/usb_device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h> /* NEU */
#include <io.h>
LOG_MODULE_REGISTER(usb, LOG_LEVEL_INF);
K_SEM_DEFINE(usb_rx_sem, 0, 1);
K_SEM_DEFINE(usb_tx_sem, 0, 1);
#define UART_NODE DT_ALIAS(usb_uart)
const struct device *cdc_dev = DEVICE_DT_GET(UART_NODE);
/* NEU: Ringbuffer für stabilen asynchronen USB-Empfang */
#define RX_RING_BUF_SIZE 4096
RING_BUF_DECLARE(rx_ringbuf, RX_RING_BUF_SIZE);
static void cdc_acm_irq_cb(const struct device *dev, void *user_data)
{
ARG_UNUSED(user_data);
if (!uart_irq_update(dev)) {
return;
}
if (uart_irq_rx_ready(dev)) {
uint8_t buffer[64];
uint32_t space = ring_buf_space_get(&rx_ringbuf);
if (space == 0) {
/* Backpressure anwenden: Ringpuffer ist voll.
Interrupt deaktivieren, damit Daten im HW-FIFO bleiben
und der USB-Stack den Host drosselt (NAK). */
uart_irq_rx_disable(dev);
} else {
/* Nur so viele Daten lesen, wie Platz im Ringpuffer ist */
int to_read = MIN(sizeof(buffer), space);
int len = uart_fifo_read(dev, buffer, to_read);
if (len > 0) {
ring_buf_put(&rx_ringbuf, buffer, len);
k_sem_give(&usb_rx_sem);
}
}
}
if (uart_irq_tx_ready(dev)) {
uart_irq_tx_disable(dev);
k_sem_give(&usb_tx_sem);
}
}
bool usb_wait_for_data(k_timeout_t timeout)
{
if (!ring_buf_is_empty(&rx_ringbuf)) {
return true;
}
/* Wenn der Puffer leer ist, sicherstellen, dass der RX-Interrupt
aktiviert ist, da sonst keine neuen Daten empfangen werden können. */
if (device_is_ready(cdc_dev)) {
uart_irq_rx_enable(cdc_dev);
}
return (k_sem_take(&usb_rx_sem, timeout) == 0);
}
int usb_read_char(uint8_t *c)
{
int ret = ring_buf_get(&rx_ringbuf, c, 1);
if (ret > 0 && device_is_ready(cdc_dev)) {
/* Platz geschaffen -> Empfang wieder aktivieren */
uart_irq_rx_enable(cdc_dev);
}
return ret;
}
int usb_read_buffer(uint8_t *buf, size_t max_len)
{
int ret = ring_buf_get(&rx_ringbuf, buf, max_len);
if (ret > 0 && device_is_ready(cdc_dev)) {
/* Platz geschaffen -> Empfang wieder aktivieren */
uart_irq_rx_enable(cdc_dev);
}
return ret;
}
void usb_resume_rx(void)
{
if (device_is_ready(cdc_dev)) {
uart_irq_rx_enable(cdc_dev);
}
}
void usb_write_char(uint8_t c)
{
if (!device_is_ready(cdc_dev)) {
return;
}
uart_poll_out(cdc_dev, c);
}
void usb_write_buffer(const uint8_t *buf, size_t len)
{
if (!device_is_ready(cdc_dev))
{
return;
}
size_t written;
while (len > 0)
{
written = uart_fifo_fill(cdc_dev, buf, len);
len -= written;
buf += written;
if (len > 0)
{
uart_irq_tx_enable(cdc_dev);
if (k_sem_take(&usb_tx_sem, K_MSEC(100)) != 0)
{
LOG_WRN("USB TX timeout - consumer not reading?");
return;
}
}
}
}
void usb_flush_rx(void)
{
uint8_t dummy;
if (!device_is_ready(cdc_dev)) return;
/* Hardware-FIFO leeren, falls Reste vorhanden */
while (uart_fifo_read(cdc_dev, &dummy, 1) > 0);
/* Ringpuffer und Semaphore zurücksetzen */
ring_buf_reset(&rx_ringbuf);
k_sem_reset(&usb_rx_sem);
}
static void usb_status_cb(enum usb_dc_status_code cb_status, const uint8_t *param)
{
switch (cb_status) {
case USB_DC_CONNECTED:
/* VBUS wurde vom Zephyr-Stack erkannt */
LOG_DBG("VBUS detected, USB device connected");
break;
case USB_DC_CONFIGURED:
LOG_DBG("USB device configured by host");
io_usb_status(true);
if (device_is_ready(cdc_dev)) {
(void)uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DCD, 1);
(void)uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DSR, 1);
/* Interrupt-Handler binden und initial aktivieren */
uart_irq_callback_set(cdc_dev, cdc_acm_irq_cb);
uart_irq_rx_enable(cdc_dev);
}
break;
case USB_DC_DISCONNECTED:
/* Kabel wurde gezogen */
LOG_DBG("VBUS removed, USB device disconnected");
if (device_is_ready(cdc_dev)) {
uart_irq_rx_disable(cdc_dev);
}
io_usb_status(false);
break;
case USB_DC_RESET:
LOG_DBG("USB bus reset");
break;
default:
break;
}
}
int usb_cdc_acm_init(void)
{
LOG_DBG("Initializing USB Stack...");
/* Zephyr-Treiber registrieren. Verbraucht keinen Strom ohne VBUS. */
int ret = usb_enable(usb_status_cb);
if (ret != 0) {
LOG_ERR("Failed to enable USB (%d)", ret);
return ret;
}
#if DT_NODE_HAS_STATUS(DT_NODELABEL(cdc_acm_uart0), okay)
const struct device *cdc_dev = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart0));
if (!device_is_ready(cdc_dev)) {
LOG_ERR("CDC ACM device not ready");
return -ENODEV;
}
#else
LOG_ERR("CDC ACM UART device not found in devicetree");
return -ENODEV;
#endif
LOG_DBG("USB Stack enabled and waiting for VBUS in hardware");
return 0;
}