xref: /freebsd/sys/dev/uart/uart_tty.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /*-
2  * Copyright (c) 2003 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/conf.h>
34 #include <sys/cons.h>
35 #include <sys/fcntl.h>
36 #include <sys/interrupt.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/reboot.h>
40 #include <machine/bus.h>
41 #include <sys/rman.h>
42 #include <sys/tty.h>
43 #include <machine/resource.h>
44 #include <machine/stdarg.h>
45 
46 #include <dev/uart/uart.h>
47 #include <dev/uart/uart_bus.h>
48 #include <dev/uart/uart_cpu.h>
49 
50 #include "uart_if.h"
51 
52 static cn_probe_t uart_cnprobe;
53 static cn_init_t uart_cninit;
54 static cn_term_t uart_cnterm;
55 static cn_getc_t uart_cngetc;
56 static cn_putc_t uart_cnputc;
57 
58 CONSOLE_DRIVER(uart);
59 
60 static struct uart_devinfo uart_console;
61 
62 static void
63 uart_cnprobe(struct consdev *cp)
64 {
65 
66 	cp->cn_pri = CN_DEAD;
67 
68 	KASSERT(uart_console.cookie == NULL, ("foo"));
69 
70 	if (uart_cpu_getdev(UART_DEV_CONSOLE, &uart_console))
71 		return;
72 
73 	if (uart_probe(&uart_console))
74 		return;
75 
76 	strlcpy(cp->cn_name, uart_driver_name, sizeof(cp->cn_name));
77 	cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
78 	cp->cn_arg = &uart_console;
79 }
80 
81 static void
82 uart_cninit(struct consdev *cp)
83 {
84 	struct uart_devinfo *di;
85 
86 	/*
87 	 * Yedi trick: we need to be able to define cn_dev before we go
88 	 * single- or multi-user. The problem is that we don't know at
89 	 * this time what the device will be. Hence, we need to link from
90 	 * the uart_devinfo to the consdev that corresponds to it so that
91 	 * we can define cn_dev in uart_bus_attach() when we find the
92 	 * device during bus enumeration. That's when we'll know what the
93 	 * the unit number will be.
94 	 */
95 	di = cp->cn_arg;
96 	KASSERT(di->cookie == NULL, ("foo"));
97 	di->cookie = cp;
98 	di->type = UART_DEV_CONSOLE;
99 	uart_add_sysdev(di);
100 	uart_init(di);
101 }
102 
103 static void
104 uart_cnterm(struct consdev *cp)
105 {
106 
107 	uart_term(cp->cn_arg);
108 }
109 
110 static void
111 uart_cnputc(struct consdev *cp, int c)
112 {
113 
114 	uart_putc(cp->cn_arg, c);
115 }
116 
117 static int
118 uart_cngetc(struct consdev *cp)
119 {
120 
121 	return (uart_poll(cp->cn_arg));
122 }
123 
124 static int
125 uart_tty_open(struct tty *tp)
126 {
127 	struct uart_softc *sc;
128 
129 	sc = tty_softc(tp);
130 
131 	if (sc == NULL || sc->sc_leaving)
132 		return (ENXIO);
133 
134 	sc->sc_opened = 1;
135 	return (0);
136 }
137 
138 static void
139 uart_tty_close(struct tty *tp)
140 {
141 	struct uart_softc *sc;
142 
143 	sc = tty_softc(tp);
144 	if (sc == NULL || sc->sc_leaving || !sc->sc_opened)
145 		return;
146 
147 	if (sc->sc_hwiflow)
148 		UART_IOCTL(sc, UART_IOCTL_IFLOW, 0);
149 	if (sc->sc_hwoflow)
150 		UART_IOCTL(sc, UART_IOCTL_OFLOW, 0);
151 	if (sc->sc_sysdev == NULL)
152 		UART_SETSIG(sc, SER_DDTR | SER_DRTS);
153 
154 	wakeup(sc);
155 	sc->sc_opened = 0;
156 	return;
157 }
158 
159 static void
160 uart_tty_outwakeup(struct tty *tp)
161 {
162 	struct uart_softc *sc;
163 
164 	sc = tty_softc(tp);
165 	if (sc == NULL || sc->sc_leaving)
166 		return;
167 
168 	if (sc->sc_txbusy)
169 		return;
170 
171 	/*
172 	 * Respect RTS/CTS (output) flow control if enabled and not already
173 	 * handled by hardware.
174 	 */
175 	if ((tp->t_termios.c_cflag & CCTS_OFLOW) && !sc->sc_hwoflow &&
176 	    !(sc->sc_hwsig & SER_CTS))
177 		return;
178 
179 	sc->sc_txdatasz = ttydisc_getc(tp, sc->sc_txbuf, sc->sc_txfifosz);
180 	if (sc->sc_txdatasz != 0)
181 		UART_TRANSMIT(sc);
182 }
183 
184 static void
185 uart_tty_inwakeup(struct tty *tp)
186 {
187 	struct uart_softc *sc;
188 
189 	sc = tty_softc(tp);
190 	if (sc == NULL || sc->sc_leaving)
191 		return;
192 
193 	if (sc->sc_isquelch) {
194 		if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow)
195 			UART_SETSIG(sc, SER_DRTS|SER_RTS);
196 		sc->sc_isquelch = 0;
197 		uart_sched_softih(sc, SER_INT_RXREADY);
198 	}
199 }
200 
201 static int
202 uart_tty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
203 {
204 	struct uart_softc *sc;
205 
206 	sc = tty_softc(tp);
207 
208 	switch (cmd) {
209 	case TIOCSBRK:
210 		UART_IOCTL(sc, UART_IOCTL_BREAK, 1);
211 		return (0);
212 	case TIOCCBRK:
213 		UART_IOCTL(sc, UART_IOCTL_BREAK, 0);
214 		return (0);
215 	default:
216 		return pps_ioctl(cmd, data, &sc->sc_pps);
217 	}
218 }
219 
220 static int
221 uart_tty_param(struct tty *tp, struct termios *t)
222 {
223 	struct uart_softc *sc;
224 	int databits, parity, stopbits;
225 
226 	sc = tty_softc(tp);
227 	if (sc == NULL || sc->sc_leaving)
228 		return (ENODEV);
229 	if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0)
230 		return (EINVAL);
231 	/* Fixate certain parameters for system devices. */
232 	if (sc->sc_sysdev != NULL) {
233 		t->c_ispeed = t->c_ospeed = sc->sc_sysdev->baudrate;
234 		t->c_cflag |= CLOCAL;
235 		t->c_cflag &= ~HUPCL;
236 	}
237 	if (t->c_ospeed == 0) {
238 		UART_SETSIG(sc, SER_DDTR | SER_DRTS);
239 		return (0);
240 	}
241 	switch (t->c_cflag & CSIZE) {
242 	case CS5:	databits = 5; break;
243 	case CS6:	databits = 6; break;
244 	case CS7:	databits = 7; break;
245 	default:	databits = 8; break;
246 	}
247 	stopbits = (t->c_cflag & CSTOPB) ? 2 : 1;
248 	if (t->c_cflag & PARENB)
249 		parity = (t->c_cflag & PARODD) ? UART_PARITY_ODD
250 		    : UART_PARITY_EVEN;
251 	else
252 		parity = UART_PARITY_NONE;
253 	if (UART_PARAM(sc, t->c_ospeed, databits, stopbits, parity) != 0)
254 		return (EINVAL);
255 	UART_SETSIG(sc, SER_DDTR | SER_DTR);
256 	/* Set input flow control state. */
257 	if (!sc->sc_hwiflow) {
258 		if ((t->c_cflag & CRTS_IFLOW) && sc->sc_isquelch)
259 			UART_SETSIG(sc, SER_DRTS);
260 		else
261 			UART_SETSIG(sc, SER_DRTS | SER_RTS);
262 	} else
263 		UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW));
264 	/* Set output flow control state. */
265 	if (sc->sc_hwoflow)
266 		UART_IOCTL(sc, UART_IOCTL_OFLOW, (t->c_cflag & CCTS_OFLOW));
267 
268 	return (0);
269 }
270 
271 static int
272 uart_tty_modem(struct tty *tp, int biton, int bitoff)
273 {
274 	struct uart_softc *sc;
275 
276 	sc = tty_softc(tp);
277 	if (biton != 0 || bitoff != 0)
278 		UART_SETSIG(sc, SER_DELTA(bitoff|biton) | biton);
279 	return (sc->sc_hwsig);
280 }
281 
282 void
283 uart_tty_intr(void *arg)
284 {
285 	struct uart_softc *sc = arg;
286 	struct tty *tp;
287 	int c, err = 0, pend, sig, xc;
288 
289 	if (sc->sc_leaving)
290 		return;
291 
292 	pend = atomic_readandclear_32(&sc->sc_ttypend);
293 	if (!(pend & SER_INT_MASK))
294 		return;
295 
296 	tp = sc->sc_u.u_tty.tp;
297 	tty_lock(tp);
298 
299 	if (pend & SER_INT_RXREADY) {
300 		while (!uart_rx_empty(sc) && !sc->sc_isquelch) {
301 			xc = uart_rx_peek(sc);
302 			c = xc & 0xff;
303 			if (xc & UART_STAT_FRAMERR)
304 				err |= TRE_FRAMING;
305 			if (xc & UART_STAT_OVERRUN)
306 				err |= TRE_OVERRUN;
307 			if (xc & UART_STAT_PARERR)
308 				err |= TRE_PARITY;
309 			if (ttydisc_rint(tp, c, err) != 0) {
310 				sc->sc_isquelch = 1;
311 				if ((tp->t_termios.c_cflag & CRTS_IFLOW) &&
312 				    !sc->sc_hwiflow)
313 					UART_SETSIG(sc, SER_DRTS);
314 			} else
315 				uart_rx_next(sc);
316 		}
317 	}
318 
319 	if (pend & SER_INT_BREAK)
320 		ttydisc_rint(tp, 0, TRE_BREAK);
321 
322 	if (pend & SER_INT_SIGCHG) {
323 		sig = pend & SER_INT_SIGMASK;
324 		if (sig & SER_DDCD)
325 			ttydisc_modem(tp, sig & SER_DCD);
326 		if (sig & SER_DCTS)
327 			uart_tty_outwakeup(tp);
328 	}
329 
330 	if (pend & SER_INT_TXIDLE)
331 		uart_tty_outwakeup(tp);
332 	ttydisc_rint_done(tp);
333 	tty_unlock(tp);
334 }
335 
336 static void
337 uart_tty_free(void *arg)
338 {
339 
340 	/*
341 	 * XXX: uart(4) could reuse the device unit number before it is
342 	 * being freed by the TTY layer. We should use this hook to free
343 	 * the device unit number, but unfortunately newbus does not
344 	 * seem to support such a construct.
345 	 */
346 }
347 
348 static struct ttydevsw uart_tty_class = {
349 	.tsw_flags	= TF_INITLOCK|TF_CALLOUT,
350 	.tsw_open	= uart_tty_open,
351 	.tsw_close	= uart_tty_close,
352 	.tsw_outwakeup	= uart_tty_outwakeup,
353 	.tsw_inwakeup	= uart_tty_inwakeup,
354 	.tsw_ioctl	= uart_tty_ioctl,
355 	.tsw_param	= uart_tty_param,
356 	.tsw_modem	= uart_tty_modem,
357 	.tsw_free	= uart_tty_free,
358 };
359 
360 int
361 uart_tty_attach(struct uart_softc *sc)
362 {
363 	struct tty *tp;
364 	int unit;
365 
366 	sc->sc_u.u_tty.tp = tp = tty_alloc(&uart_tty_class, sc);
367 
368 	unit = device_get_unit(sc->sc_dev);
369 
370 	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
371 		sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name,
372 		    "ttyu%r", unit);
373 		tty_init_console(tp, 0);
374 	}
375 
376 	swi_add(&tty_intr_event, uart_driver_name, uart_tty_intr, sc, SWI_TTY,
377 	    INTR_TYPE_TTY, &sc->sc_softih);
378 
379 	tty_makedev(tp, NULL, "u%r", unit);
380 
381 	return (0);
382 }
383 
384 int
385 uart_tty_detach(struct uart_softc *sc)
386 {
387 	struct tty *tp;
388 
389 	tp = sc->sc_u.u_tty.tp;
390 
391 	tty_lock(tp);
392 	swi_remove(sc->sc_softih);
393 	tty_rel_gone(tp);
394 
395 	return (0);
396 }
397