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