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