207 lines
5.3 KiB
C
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;
|
|
} |