#include #include #include #include #include /* NEU */ #include 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; }