]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/uart/uart_tty.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / uart / uart_tty.c
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 static cn_grab_t uart_cngrab;
58 static cn_ungrab_t uart_cnungrab;
59
60 CONSOLE_DRIVER(uart);
61
62 static struct uart_devinfo uart_console;
63
64 static void
65 uart_cnprobe(struct consdev *cp)
66 {
67
68         cp->cn_pri = CN_DEAD;
69
70         KASSERT(uart_console.cookie == NULL, ("foo"));
71
72         if (uart_cpu_getdev(UART_DEV_CONSOLE, &uart_console))
73                 return;
74
75         if (uart_probe(&uart_console))
76                 return;
77
78         strlcpy(cp->cn_name, uart_driver_name, sizeof(cp->cn_name));
79         cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
80         cp->cn_arg = &uart_console;
81 }
82
83 static void
84 uart_cninit(struct consdev *cp)
85 {
86         struct uart_devinfo *di;
87
88         /*
89          * Yedi trick: we need to be able to define cn_dev before we go
90          * single- or multi-user. The problem is that we don't know at
91          * this time what the device will be. Hence, we need to link from
92          * the uart_devinfo to the consdev that corresponds to it so that
93          * we can define cn_dev in uart_bus_attach() when we find the
94          * device during bus enumeration. That's when we'll know what the
95          * the unit number will be.
96          */
97         di = cp->cn_arg;
98         KASSERT(di->cookie == NULL, ("foo"));
99         di->cookie = cp;
100         di->type = UART_DEV_CONSOLE;
101         uart_add_sysdev(di);
102         uart_init(di);
103 }
104
105 static void
106 uart_cnterm(struct consdev *cp)
107 {
108
109         uart_term(cp->cn_arg);
110 }
111
112 static void
113 uart_cngrab(struct consdev *cp)
114 {
115
116         uart_grab(cp->cn_arg);
117 }
118
119 static void
120 uart_cnungrab(struct consdev *cp)
121 {
122
123         uart_ungrab(cp->cn_arg);
124 }
125
126 static void
127 uart_cnputc(struct consdev *cp, int c)
128 {
129
130         uart_putc(cp->cn_arg, c);
131 }
132
133 static int
134 uart_cngetc(struct consdev *cp)
135 {
136
137         return (uart_poll(cp->cn_arg));
138 }
139
140 static int
141 uart_tty_open(struct tty *tp)
142 {
143         struct uart_softc *sc;
144
145         sc = tty_softc(tp);
146
147         if (sc == NULL || sc->sc_leaving)
148                 return (ENXIO);
149
150         sc->sc_opened = 1;
151         return (0);
152 }
153
154 static void
155 uart_tty_close(struct tty *tp)
156 {
157         struct uart_softc *sc;
158
159         sc = tty_softc(tp);
160         if (sc == NULL || sc->sc_leaving || !sc->sc_opened) 
161                 return;
162
163         if (sc->sc_hwiflow)
164                 UART_IOCTL(sc, UART_IOCTL_IFLOW, 0);
165         if (sc->sc_hwoflow)
166                 UART_IOCTL(sc, UART_IOCTL_OFLOW, 0);
167         if (sc->sc_sysdev == NULL)
168                 UART_SETSIG(sc, SER_DDTR | SER_DRTS);
169
170         wakeup(sc);
171         sc->sc_opened = 0;
172         return;
173 }
174
175 static void
176 uart_tty_outwakeup(struct tty *tp)
177 {
178         struct uart_softc *sc;
179
180         sc = tty_softc(tp);
181         if (sc == NULL || sc->sc_leaving)
182                 return;
183
184         if (sc->sc_txbusy)
185                 return;
186
187         /*
188          * Respect RTS/CTS (output) flow control if enabled and not already
189          * handled by hardware.
190          */
191         if ((tp->t_termios.c_cflag & CCTS_OFLOW) && !sc->sc_hwoflow &&
192             !(sc->sc_hwsig & SER_CTS))
193                 return;
194
195         sc->sc_txdatasz = ttydisc_getc(tp, sc->sc_txbuf, sc->sc_txfifosz);
196         if (sc->sc_txdatasz != 0)
197                 UART_TRANSMIT(sc);
198 }
199
200 static void
201 uart_tty_inwakeup(struct tty *tp)
202 {
203         struct uart_softc *sc;
204
205         sc = tty_softc(tp);
206         if (sc == NULL || sc->sc_leaving)
207                 return;
208
209         if (sc->sc_isquelch) {
210                 if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow)
211                         UART_SETSIG(sc, SER_DRTS|SER_RTS);
212                 sc->sc_isquelch = 0;
213                 uart_sched_softih(sc, SER_INT_RXREADY);
214         }
215 }
216
217 static int
218 uart_tty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
219 {
220         struct uart_softc *sc;
221
222         sc = tty_softc(tp);
223
224         switch (cmd) {
225         case TIOCSBRK:
226                 UART_IOCTL(sc, UART_IOCTL_BREAK, 1);
227                 return (0);
228         case TIOCCBRK:
229                 UART_IOCTL(sc, UART_IOCTL_BREAK, 0);
230                 return (0);
231         default:
232                 return pps_ioctl(cmd, data, &sc->sc_pps);
233         }
234 }
235
236 static int
237 uart_tty_param(struct tty *tp, struct termios *t)
238 {
239         struct uart_softc *sc;
240         int databits, parity, stopbits;
241
242         sc = tty_softc(tp);
243         if (sc == NULL || sc->sc_leaving)
244                 return (ENODEV);
245         if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0)
246                 return (EINVAL);
247         /* Fixate certain parameters for system devices. */
248         if (sc->sc_sysdev != NULL) {
249                 t->c_ispeed = t->c_ospeed = sc->sc_sysdev->baudrate;
250                 t->c_cflag |= CLOCAL;
251                 t->c_cflag &= ~HUPCL;
252         }
253         if (t->c_ospeed == 0) {
254                 UART_SETSIG(sc, SER_DDTR | SER_DRTS);
255                 return (0);
256         }
257         switch (t->c_cflag & CSIZE) {
258         case CS5:       databits = 5; break;
259         case CS6:       databits = 6; break;
260         case CS7:       databits = 7; break;
261         default:        databits = 8; break;
262         }
263         stopbits = (t->c_cflag & CSTOPB) ? 2 : 1;
264         if (t->c_cflag & PARENB)
265                 parity = (t->c_cflag & PARODD) ? UART_PARITY_ODD
266                     : UART_PARITY_EVEN;
267         else
268                 parity = UART_PARITY_NONE;
269         if (UART_PARAM(sc, t->c_ospeed, databits, stopbits, parity) != 0)
270                 return (EINVAL);
271         UART_SETSIG(sc, SER_DDTR | SER_DTR);
272         /* Set input flow control state. */
273         if (!sc->sc_hwiflow) {
274                 if ((t->c_cflag & CRTS_IFLOW) && sc->sc_isquelch)
275                         UART_SETSIG(sc, SER_DRTS);
276                 else
277                         UART_SETSIG(sc, SER_DRTS | SER_RTS);
278         } else
279                 UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW));
280         /* Set output flow control state. */
281         if (sc->sc_hwoflow)
282                 UART_IOCTL(sc, UART_IOCTL_OFLOW, (t->c_cflag & CCTS_OFLOW));
283
284         return (0);
285 }
286
287 static int
288 uart_tty_modem(struct tty *tp, int biton, int bitoff)
289 {
290         struct uart_softc *sc;
291
292         sc = tty_softc(tp);
293         if (biton != 0 || bitoff != 0)
294                 UART_SETSIG(sc, SER_DELTA(bitoff|biton) | biton);
295         return (sc->sc_hwsig);
296 }
297
298 void
299 uart_tty_intr(void *arg)
300 {
301         struct uart_softc *sc = arg;
302         struct tty *tp;
303         int c, err = 0, pend, sig, xc;
304
305         if (sc->sc_leaving)
306                 return;
307
308         pend = atomic_readandclear_32(&sc->sc_ttypend);
309         if (!(pend & SER_INT_MASK))
310                 return;
311
312         tp = sc->sc_u.u_tty.tp;
313         tty_lock(tp);
314
315         if (pend & SER_INT_RXREADY) {
316                 while (!uart_rx_empty(sc) && !sc->sc_isquelch) {
317                         xc = uart_rx_peek(sc);
318                         c = xc & 0xff;
319                         if (xc & UART_STAT_FRAMERR)
320                                 err |= TRE_FRAMING;
321                         if (xc & UART_STAT_OVERRUN)
322                                 err |= TRE_OVERRUN;
323                         if (xc & UART_STAT_PARERR)
324                                 err |= TRE_PARITY;
325                         if (ttydisc_rint(tp, c, err) != 0) {
326                                 sc->sc_isquelch = 1;
327                                 if ((tp->t_termios.c_cflag & CRTS_IFLOW) &&
328                                     !sc->sc_hwiflow)
329                                         UART_SETSIG(sc, SER_DRTS);
330                         } else
331                                 uart_rx_next(sc);
332                 }
333         }
334
335         if (pend & SER_INT_BREAK)
336                 ttydisc_rint(tp, 0, TRE_BREAK);
337
338         if (pend & SER_INT_SIGCHG) {
339                 sig = pend & SER_INT_SIGMASK;
340                 if (sig & SER_DDCD)
341                         ttydisc_modem(tp, sig & SER_DCD);
342                 if (sig & SER_DCTS)
343                         uart_tty_outwakeup(tp);
344         }
345
346         if (pend & SER_INT_TXIDLE)
347                 uart_tty_outwakeup(tp);
348         ttydisc_rint_done(tp);
349         tty_unlock(tp);
350 }
351
352 static void
353 uart_tty_free(void *arg)
354 {
355
356         /*
357          * XXX: uart(4) could reuse the device unit number before it is
358          * being freed by the TTY layer. We should use this hook to free
359          * the device unit number, but unfortunately newbus does not
360          * seem to support such a construct.
361          */
362 }
363
364 static struct ttydevsw uart_tty_class = {
365         .tsw_flags      = TF_INITLOCK|TF_CALLOUT,
366         .tsw_open       = uart_tty_open,
367         .tsw_close      = uart_tty_close,
368         .tsw_outwakeup  = uart_tty_outwakeup,
369         .tsw_inwakeup   = uart_tty_inwakeup,
370         .tsw_ioctl      = uart_tty_ioctl,
371         .tsw_param      = uart_tty_param,
372         .tsw_modem      = uart_tty_modem,
373         .tsw_free       = uart_tty_free,
374 };
375
376 int
377 uart_tty_attach(struct uart_softc *sc)
378 {
379         struct tty *tp;
380         int unit;
381
382         sc->sc_u.u_tty.tp = tp = tty_alloc(&uart_tty_class, sc);
383
384         unit = device_get_unit(sc->sc_dev);
385
386         if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
387                 sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name,
388                     "ttyu%r", unit);
389                 tty_init_console(tp, 0);
390         }
391
392         swi_add(&tty_intr_event, uart_driver_name, uart_tty_intr, sc, SWI_TTY,
393             INTR_TYPE_TTY, &sc->sc_softih);
394
395         tty_makedev(tp, NULL, "u%r", unit);
396
397         return (0);
398 }
399
400 int
401 uart_tty_detach(struct uart_softc *sc)
402 {
403         struct tty *tp;
404
405         tp = sc->sc_u.u_tty.tp;
406
407         tty_lock(tp);
408         swi_remove(sc->sc_softih);
409         tty_rel_gone(tp);
410
411         return (0);
412 }