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