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