1 /*
2  * Copyright (c) 2018 Linaro Limited.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr.h>
8 #include <drivers/uart.h>
9 #include <sys/printk.h>
10 #include <console/tty.h>
11 
12 static int tty_irq_input_hook(struct tty_serial *tty, uint8_t c);
13 static int tty_putchar(struct tty_serial *tty, uint8_t c);
14 
tty_uart_isr(const struct device * dev,void * user_data)15 static void tty_uart_isr(const struct device *dev, void *user_data)
16 {
17 	struct tty_serial *tty = user_data;
18 
19 	uart_irq_update(dev);
20 
21 	if (uart_irq_rx_ready(dev)) {
22 		uint8_t c;
23 
24 		while (1) {
25 			if (uart_fifo_read(dev, &c, 1) == 0) {
26 				break;
27 			}
28 			tty_irq_input_hook(tty, c);
29 		}
30 	}
31 
32 	if (uart_irq_tx_ready(dev)) {
33 		if (tty->tx_get == tty->tx_put) {
34 			/* Output buffer empty, don't bother
35 			 * us with tx interrupts
36 			 */
37 			uart_irq_tx_disable(dev);
38 		} else {
39 			uart_fifo_fill(dev, &tty->tx_ringbuf[tty->tx_get++], 1);
40 			if (tty->tx_get >= tty->tx_ringbuf_sz) {
41 				tty->tx_get = 0U;
42 			}
43 			k_sem_give(&tty->tx_sem);
44 		}
45 	}
46 }
47 
tty_irq_input_hook(struct tty_serial * tty,uint8_t c)48 static int tty_irq_input_hook(struct tty_serial *tty, uint8_t c)
49 {
50 	int rx_next = tty->rx_put + 1;
51 
52 	if (rx_next >= tty->rx_ringbuf_sz) {
53 		rx_next = 0;
54 	}
55 
56 	if (rx_next == tty->rx_get) {
57 		/* Try to give a clue to user that some input was lost */
58 		tty_putchar(tty, '~');
59 		return 1;
60 	}
61 
62 	tty->rx_ringbuf[tty->rx_put] = c;
63 	tty->rx_put = rx_next;
64 	k_sem_give(&tty->rx_sem);
65 
66 	return 1;
67 }
68 
tty_putchar(struct tty_serial * tty,uint8_t c)69 static int tty_putchar(struct tty_serial *tty, uint8_t c)
70 {
71 	unsigned int key;
72 	int tx_next;
73 	int res;
74 
75 	res = k_sem_take(&tty->tx_sem,
76 			 k_is_in_isr() ? K_NO_WAIT :
77 					 SYS_TIMEOUT_MS(tty->tx_timeout));
78 	if (res < 0) {
79 		return res;
80 	}
81 
82 	key = irq_lock();
83 	tx_next = tty->tx_put + 1;
84 	if (tx_next >= tty->tx_ringbuf_sz) {
85 		tx_next = 0;
86 	}
87 	if (tx_next == tty->tx_get) {
88 		irq_unlock(key);
89 		return -ENOSPC;
90 	}
91 
92 	tty->tx_ringbuf[tty->tx_put] = c;
93 	tty->tx_put = tx_next;
94 
95 	irq_unlock(key);
96 	uart_irq_tx_enable(tty->uart_dev);
97 	return 0;
98 }
99 
tty_write(struct tty_serial * tty,const void * buf,size_t size)100 ssize_t tty_write(struct tty_serial *tty, const void *buf, size_t size)
101 {
102 	const uint8_t *p = buf;
103 	size_t out_size = 0;
104 	int res = 0;
105 
106 	if (tty->tx_ringbuf_sz == 0U) {
107 		/* Unbuffered operation, implicitly blocking. */
108 		out_size = size;
109 
110 		while (size--) {
111 			uart_poll_out(tty->uart_dev, *p++);
112 		}
113 
114 		return out_size;
115 	}
116 
117 	while (size--) {
118 		res = tty_putchar(tty, *p++);
119 		if (res < 0) {
120 			/* If we didn't transmit anything, return the error. */
121 			if (out_size == 0) {
122 				errno = -res;
123 				return res;
124 			}
125 
126 			/*
127 			 * Otherwise, return how much we transmitted. If error
128 			 * was transient (like EAGAIN), on next call user might
129 			 * not even get it. And if it's non-transient, they'll
130 			 * get it on the next call.
131 			 */
132 			return out_size;
133 		}
134 
135 		out_size++;
136 	}
137 
138 	return out_size;
139 }
140 
tty_getchar(struct tty_serial * tty)141 static int tty_getchar(struct tty_serial *tty)
142 {
143 	unsigned int key;
144 	uint8_t c;
145 	int res;
146 
147 	res = k_sem_take(&tty->rx_sem, SYS_TIMEOUT_MS(tty->rx_timeout));
148 	if (res < 0) {
149 		return res;
150 	}
151 
152 	key = irq_lock();
153 	c = tty->rx_ringbuf[tty->rx_get++];
154 	if (tty->rx_get >= tty->rx_ringbuf_sz) {
155 		tty->rx_get = 0U;
156 	}
157 	irq_unlock(key);
158 
159 	return c;
160 }
161 
tty_read_unbuf(struct tty_serial * tty,void * buf,size_t size)162 static ssize_t tty_read_unbuf(struct tty_serial *tty, void *buf, size_t size)
163 {
164 	uint8_t *p = buf;
165 	size_t out_size = 0;
166 	int res = 0;
167 	uint32_t timeout = tty->rx_timeout;
168 
169 	while (size) {
170 		uint8_t c;
171 		res = uart_poll_in(tty->uart_dev, &c);
172 		if (res <= -2) {
173 			/* Error occurred, best we can do is to return
174 			 * accumulated data w/o error, or return error
175 			 * directly if none.
176 			 */
177 			if (out_size == 0) {
178 				errno = res;
179 				return -1;
180 			}
181 			break;
182 		}
183 
184 		if (res == 0) {
185 			*p++ = c;
186 			out_size++;
187 			size--;
188 		}
189 
190 		if (size == 0 ||
191 		    ((timeout != SYS_FOREVER_MS) && timeout-- == 0U)) {
192 			break;
193 		}
194 
195 		/* Avoid 100% busy-polling, and yet try to process bursts
196 		 * of data without extra delays.
197 		 */
198 		if (res == -1) {
199 			k_sleep(K_MSEC(1));
200 		}
201 	}
202 
203 	return out_size;
204 }
205 
tty_read(struct tty_serial * tty,void * buf,size_t size)206 ssize_t tty_read(struct tty_serial *tty, void *buf, size_t size)
207 {
208 	uint8_t *p = buf;
209 	size_t out_size = 0;
210 	int res = 0;
211 
212 	if (tty->rx_ringbuf_sz == 0U) {
213 		return tty_read_unbuf(tty, buf, size);
214 	}
215 
216 	while (size--) {
217 		res = tty_getchar(tty);
218 		if (res < 0) {
219 			/* If we didn't transmit anything, return the error. */
220 			if (out_size == 0) {
221 				errno = -res;
222 				return res;
223 			}
224 
225 			/*
226 			 * Otherwise, return how much we transmitted. If error
227 			 * was transient (like EAGAIN), on next call user might
228 			 * not even get it. And if it's non-transient, they'll
229 			 * get it on the next call.
230 			 */
231 			return out_size;
232 		}
233 
234 		*p++ = (uint8_t)res;
235 		out_size++;
236 	}
237 
238 	return out_size;
239 }
240 
tty_init(struct tty_serial * tty,const struct device * uart_dev)241 int tty_init(struct tty_serial *tty, const struct device *uart_dev)
242 {
243 	if (!uart_dev) {
244 		return -ENODEV;
245 	}
246 
247 	tty->uart_dev = uart_dev;
248 
249 	/* We start in unbuffer mode. */
250 	tty->rx_ringbuf = NULL;
251 	tty->rx_ringbuf_sz = 0U;
252 	tty->tx_ringbuf = NULL;
253 	tty->tx_ringbuf_sz = 0U;
254 
255 	tty->rx_get = tty->rx_put = tty->tx_get = tty->tx_put = 0U;
256 
257 	tty->rx_timeout = SYS_FOREVER_MS;
258 	tty->tx_timeout = SYS_FOREVER_MS;
259 
260 	uart_irq_callback_user_data_set(uart_dev, tty_uart_isr, tty);
261 
262 	return 0;
263 }
264 
tty_set_rx_buf(struct tty_serial * tty,void * buf,size_t size)265 int tty_set_rx_buf(struct tty_serial *tty, void *buf, size_t size)
266 {
267 	uart_irq_rx_disable(tty->uart_dev);
268 
269 	tty->rx_ringbuf = buf;
270 	tty->rx_ringbuf_sz = size;
271 
272 	if (size > 0) {
273 		k_sem_init(&tty->rx_sem, 0, K_SEM_MAX_LIMIT);
274 		uart_irq_rx_enable(tty->uart_dev);
275 	}
276 
277 	return 0;
278 }
279 
tty_set_tx_buf(struct tty_serial * tty,void * buf,size_t size)280 int tty_set_tx_buf(struct tty_serial *tty, void *buf, size_t size)
281 {
282 	uart_irq_tx_disable(tty->uart_dev);
283 
284 	tty->tx_ringbuf = buf;
285 	tty->tx_ringbuf_sz = size;
286 
287 	k_sem_init(&tty->tx_sem, size - 1, K_SEM_MAX_LIMIT);
288 
289 	/* New buffer is initially empty, no need to re-enable interrupts,
290 	 * it will be done when needed (on first output char).
291 	 */
292 
293 	return 0;
294 }
295