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 <stdio.h>
16 #include <string.h>
17 #include <zephyr/device.h>
18 #include <zephyr/drivers/uart.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/sys/ring_buffer.h>
21 
22 #include <zephyr/usb/usb_device.h>
23 #include <zephyr/usb/usbd.h>
24 #include <zephyr/logging/log.h>
25 LOG_MODULE_REGISTER(cdc_acm_echo, LOG_LEVEL_INF);
26 
27 #define RING_BUF_SIZE 1024
28 uint8_t ring_buffer[RING_BUF_SIZE];
29 
30 struct ring_buf ringbuf;
31 
32 #if defined(CONFIG_USB_DEVICE_STACK_NEXT)
33 USBD_CONFIGURATION_DEFINE(config_1,
34 			  USB_SCD_SELF_POWERED,
35 			  200);
36 
37 USBD_DESC_LANG_DEFINE(sample_lang);
38 USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, "ZEPHYR");
39 USBD_DESC_PRODUCT_DEFINE(sample_product, "Zephyr USBD CDC ACM");
40 USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn, "0123456789ABCDEF");
41 
42 USBD_DEVICE_DEFINE(sample_usbd,
43 		   DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
44 		   0x2fe3, 0x0001);
45 
enable_usb_device_next(void)46 static int enable_usb_device_next(void)
47 {
48 	int err;
49 
50 	err = usbd_add_descriptor(&sample_usbd, &sample_lang);
51 	if (err) {
52 		LOG_ERR("Failed to initialize language descriptor (%d)", err);
53 		return err;
54 	}
55 
56 	err = usbd_add_descriptor(&sample_usbd, &sample_mfr);
57 	if (err) {
58 		LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err);
59 		return err;
60 	}
61 
62 	err = usbd_add_descriptor(&sample_usbd, &sample_product);
63 	if (err) {
64 		LOG_ERR("Failed to initialize product descriptor (%d)", err);
65 		return err;
66 	}
67 
68 	err = usbd_add_descriptor(&sample_usbd, &sample_sn);
69 	if (err) {
70 		LOG_ERR("Failed to initialize SN descriptor (%d)", err);
71 		return err;
72 	}
73 
74 	err = usbd_add_configuration(&sample_usbd, &config_1);
75 	if (err) {
76 		LOG_ERR("Failed to add configuration (%d)", err);
77 		return err;
78 	}
79 
80 	err = usbd_register_class(&sample_usbd, "cdc_acm_0", 1);
81 	if (err) {
82 		LOG_ERR("Failed to register CDC ACM class (%d)", err);
83 		return err;
84 	}
85 
86 	err = usbd_init(&sample_usbd);
87 	if (err) {
88 		LOG_ERR("Failed to initialize device support");
89 		return err;
90 	}
91 
92 	err = usbd_enable(&sample_usbd);
93 	if (err) {
94 		LOG_ERR("Failed to enable device support");
95 		return err;
96 	}
97 
98 	LOG_DBG("USB device support enabled");
99 
100 	return 0;
101 }
102 #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK_NEXT) */
103 
interrupt_handler(const struct device * dev,void * user_data)104 static void interrupt_handler(const struct device *dev, void *user_data)
105 {
106 	ARG_UNUSED(user_data);
107 
108 	while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
109 		if (uart_irq_rx_ready(dev)) {
110 			int recv_len, rb_len;
111 			uint8_t buffer[64];
112 			size_t len = MIN(ring_buf_space_get(&ringbuf),
113 					 sizeof(buffer));
114 
115 			recv_len = uart_fifo_read(dev, buffer, len);
116 			if (recv_len < 0) {
117 				LOG_ERR("Failed to read UART FIFO");
118 				recv_len = 0;
119 			};
120 
121 			rb_len = ring_buf_put(&ringbuf, buffer, recv_len);
122 			if (rb_len < recv_len) {
123 				LOG_ERR("Drop %u bytes", recv_len - rb_len);
124 			}
125 
126 			LOG_DBG("tty fifo -> ringbuf %d bytes", rb_len);
127 			if (rb_len) {
128 				uart_irq_tx_enable(dev);
129 			}
130 		}
131 
132 		if (uart_irq_tx_ready(dev)) {
133 			uint8_t buffer[64];
134 			int rb_len, send_len;
135 
136 			rb_len = ring_buf_get(&ringbuf, buffer, sizeof(buffer));
137 			if (!rb_len) {
138 				LOG_DBG("Ring buffer empty, disable TX IRQ");
139 				uart_irq_tx_disable(dev);
140 				continue;
141 			}
142 
143 			send_len = uart_fifo_fill(dev, buffer, rb_len);
144 			if (send_len < rb_len) {
145 				LOG_ERR("Drop %d bytes", rb_len - send_len);
146 			}
147 
148 			LOG_DBG("ringbuf -> tty fifo %d bytes", send_len);
149 		}
150 	}
151 }
152 
main(void)153 int main(void)
154 {
155 	const struct device *dev;
156 	uint32_t baudrate, dtr = 0U;
157 	int ret;
158 
159 	dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
160 	if (!device_is_ready(dev)) {
161 		LOG_ERR("CDC ACM device not ready");
162 		return 0;
163 	}
164 
165 #if defined(CONFIG_USB_DEVICE_STACK_NEXT)
166 		ret = enable_usb_device_next();
167 #else
168 		ret = usb_enable(NULL);
169 #endif
170 
171 	if (ret != 0) {
172 		LOG_ERR("Failed to enable USB");
173 		return 0;
174 	}
175 
176 	ring_buf_init(&ringbuf, sizeof(ring_buffer), ring_buffer);
177 
178 	LOG_INF("Wait for DTR");
179 
180 	while (true) {
181 		uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr);
182 		if (dtr) {
183 			break;
184 		} else {
185 			/* Give CPU resources to low priority threads. */
186 			k_sleep(K_MSEC(100));
187 		}
188 	}
189 
190 	LOG_INF("DTR set");
191 
192 	/* They are optional, we use them to test the interrupt endpoint */
193 	ret = uart_line_ctrl_set(dev, UART_LINE_CTRL_DCD, 1);
194 	if (ret) {
195 		LOG_WRN("Failed to set DCD, ret code %d", ret);
196 	}
197 
198 	ret = uart_line_ctrl_set(dev, UART_LINE_CTRL_DSR, 1);
199 	if (ret) {
200 		LOG_WRN("Failed to set DSR, ret code %d", ret);
201 	}
202 
203 	/* Wait 100ms for the host to do all settings */
204 	k_msleep(100);
205 
206 	ret = uart_line_ctrl_get(dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
207 	if (ret) {
208 		LOG_WRN("Failed to get baudrate, ret code %d", ret);
209 	} else {
210 		LOG_INF("Baudrate detected: %d", baudrate);
211 	}
212 
213 	uart_irq_callback_set(dev, interrupt_handler);
214 
215 	/* Enable rx interrupts */
216 	uart_irq_rx_enable(dev);
217 	return 0;
218 }
219