1 /*
2 * Copyright (c) 2019 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <sample_usbd.h>
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/uart.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/sys/ring_buffer.h>
15
16 #include <zephyr/usb/usbd.h>
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(cdc_acm_echo, LOG_LEVEL_INF);
19
20 const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
21
22 #define RING_BUF_SIZE 1024
23 uint8_t ring_buffer[RING_BUF_SIZE];
24
25 struct ring_buf ringbuf;
26
27 static bool rx_throttled;
28
print_baudrate(const struct device * dev)29 static inline void print_baudrate(const struct device *dev)
30 {
31 uint32_t baudrate;
32 int ret;
33
34 ret = uart_line_ctrl_get(dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
35 if (ret) {
36 LOG_WRN("Failed to get baudrate, ret code %d", ret);
37 } else {
38 LOG_INF("Baudrate %u", baudrate);
39 }
40 }
41
42 static struct usbd_context *sample_usbd;
43 K_SEM_DEFINE(dtr_sem, 0, 1);
44
sample_msg_cb(struct usbd_context * const ctx,const struct usbd_msg * msg)45 static void sample_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg)
46 {
47 LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));
48
49 if (usbd_can_detect_vbus(ctx)) {
50 if (msg->type == USBD_MSG_VBUS_READY) {
51 if (usbd_enable(ctx)) {
52 LOG_ERR("Failed to enable device support");
53 }
54 }
55
56 if (msg->type == USBD_MSG_VBUS_REMOVED) {
57 if (usbd_disable(ctx)) {
58 LOG_ERR("Failed to disable device support");
59 }
60 }
61 }
62
63 if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) {
64 uint32_t dtr = 0U;
65
66 uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr);
67 if (dtr) {
68 k_sem_give(&dtr_sem);
69 }
70 }
71
72 if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) {
73 print_baudrate(msg->dev);
74 }
75 }
76
enable_usb_device_next(void)77 static int enable_usb_device_next(void)
78 {
79 int err;
80
81 sample_usbd = sample_usbd_init_device(sample_msg_cb);
82 if (sample_usbd == NULL) {
83 LOG_ERR("Failed to initialize USB device");
84 return -ENODEV;
85 }
86
87 if (!usbd_can_detect_vbus(sample_usbd)) {
88 err = usbd_enable(sample_usbd);
89 if (err) {
90 LOG_ERR("Failed to enable device support");
91 return err;
92 }
93 }
94
95 LOG_INF("USB device support enabled");
96
97 return 0;
98 }
99
interrupt_handler(const struct device * dev,void * user_data)100 static void interrupt_handler(const struct device *dev, void *user_data)
101 {
102 ARG_UNUSED(user_data);
103
104 while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
105 if (!rx_throttled && uart_irq_rx_ready(dev)) {
106 int recv_len, rb_len;
107 uint8_t buffer[64];
108 size_t len = MIN(ring_buf_space_get(&ringbuf),
109 sizeof(buffer));
110
111 if (len == 0) {
112 /* Throttle because ring buffer is full */
113 uart_irq_rx_disable(dev);
114 rx_throttled = true;
115 continue;
116 }
117
118 recv_len = uart_fifo_read(dev, buffer, len);
119 if (recv_len < 0) {
120 LOG_ERR("Failed to read UART FIFO");
121 recv_len = 0;
122 };
123
124 rb_len = ring_buf_put(&ringbuf, buffer, recv_len);
125 if (rb_len < recv_len) {
126 LOG_ERR("Drop %u bytes", recv_len - rb_len);
127 }
128
129 LOG_DBG("tty fifo -> ringbuf %d bytes", rb_len);
130 if (rb_len) {
131 uart_irq_tx_enable(dev);
132 }
133 }
134
135 if (uart_irq_tx_ready(dev)) {
136 uint8_t buffer[64];
137 int rb_len, send_len;
138
139 rb_len = ring_buf_get(&ringbuf, buffer, sizeof(buffer));
140 if (!rb_len) {
141 LOG_DBG("Ring buffer empty, disable TX IRQ");
142 uart_irq_tx_disable(dev);
143 continue;
144 }
145
146 if (rx_throttled) {
147 uart_irq_rx_enable(dev);
148 rx_throttled = false;
149 }
150
151 send_len = uart_fifo_fill(dev, buffer, rb_len);
152 if (send_len < rb_len) {
153 LOG_ERR("Drop %d bytes", rb_len - send_len);
154 }
155
156 LOG_DBG("ringbuf -> tty fifo %d bytes", send_len);
157 }
158 }
159 }
160
main(void)161 int main(void)
162 {
163 int ret;
164
165 if (!device_is_ready(uart_dev)) {
166 LOG_ERR("CDC ACM device not ready");
167 return 0;
168 }
169
170 ret = enable_usb_device_next();
171 if (ret != 0) {
172 LOG_ERR("Failed to enable USB device support");
173 return 0;
174 }
175
176 ring_buf_init(&ringbuf, sizeof(ring_buffer), ring_buffer);
177
178 LOG_INF("Wait for DTR");
179 k_sem_take(&dtr_sem, K_FOREVER);
180 LOG_INF("DTR set");
181
182 /* They are optional, we use them to test the interrupt endpoint */
183 ret = uart_line_ctrl_set(uart_dev, UART_LINE_CTRL_DCD, 1);
184 if (ret) {
185 LOG_WRN("Failed to set DCD, ret code %d", ret);
186 }
187
188 ret = uart_line_ctrl_set(uart_dev, UART_LINE_CTRL_DSR, 1);
189 if (ret) {
190 LOG_WRN("Failed to set DSR, ret code %d", ret);
191 }
192
193 /* Wait 100ms for the host to do all settings */
194 k_msleep(100);
195
196 uart_irq_callback_set(uart_dev, interrupt_handler);
197 /* Enable rx interrupts */
198 uart_irq_rx_enable(uart_dev);
199
200 return 0;
201 }
202