1/* $NetBSD$ */
2
3/*-
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD$");
31
32#include <sys/param.h>
33#include <sys/conf.h>
34#include <sys/proc.h>
35#include <sys/systm.h>
36#include <sys/device.h>
37#include <sys/kauth.h>
38#include <sys/termios.h>
39#include <sys/tty.h>
40
41#include <dev/cons.h>
42
43#include <machine/mainbus.h>
44#include <machine/thunk.h>
45
46static int	ttycons_match(device_t, cfdata_t, void *);
47static void	ttycons_attach(device_t, device_t, void *);
48
49void		ttycons_consinit(void);
50
51struct ttycons_softc {
52	device_t	sc_dev;
53	struct tty	*sc_tty;
54	void		*sc_rd_sih;
55	void		*sc_ctrlc_sih;
56	void		*sc_ctrlz_sih;
57	u_char		sc_buf[1024];
58};
59
60dev_type_cngetc(ttycons_cngetc);
61dev_type_cnputc(ttycons_cnputc);
62dev_type_cnpollc(ttycons_cnpollc);
63
64static struct cnm_state ttycons_cnm_state;
65struct consdev ttycons_consdev = {
66	.cn_getc = ttycons_cngetc,
67	.cn_putc = ttycons_cnputc,
68	.cn_pollc = ttycons_cnpollc,
69	.cn_dev = NODEV,
70	.cn_pri = CN_NORMAL,
71};
72
73CFATTACH_DECL_NEW(ttycons, sizeof(struct ttycons_softc),
74    ttycons_match, ttycons_attach, NULL, NULL);
75
76extern struct cfdriver ttycons_cd;
77
78dev_type_open(ttycons_open);
79dev_type_close(ttycons_close);
80dev_type_read(ttycons_read);
81dev_type_write(ttycons_write);
82dev_type_ioctl(ttycons_ioctl);
83dev_type_stop(ttycons_stop);
84dev_type_tty(ttycons_tty);
85dev_type_poll(ttycons_poll);
86
87const struct cdevsw ttycons_cdevsw = {
88	.d_open = ttycons_open,
89	.d_close = ttycons_close,
90	.d_read = ttycons_read,
91	.d_write = ttycons_write,
92	.d_ioctl = ttycons_ioctl,
93	.d_stop = ttycons_stop,
94	.d_tty = ttycons_tty,
95	.d_poll = ttycons_poll,
96	.d_kqfilter = ttykqfilter,
97	.d_flag = D_TTY,
98};
99
100static void	ttycons_start(struct tty *);
101static int	ttycons_param(struct tty *, struct termios *);
102
103static int	ttycons_intr(void *);
104static void	ttycons_softintr(void *);
105
106static sigfunc_t  ttycons_ctrlc;
107static void	ttycons_softctrlc(void *);
108static sigfunc_t  ttycons_ctrlz;
109static void	ttycons_softctrlz(void *);
110
111static int
112ttycons_match(device_t parent, cfdata_t match, void *opaque)
113{
114	struct thunkbus_attach_args *taa = opaque;
115
116	if (taa->taa_type != THUNKBUS_TYPE_TTYCONS)
117		return 0;
118
119	return 1;
120}
121
122static void
123ttycons_attach(device_t parent, device_t self, void *opaque)
124{
125	struct ttycons_softc *sc = device_private(self);
126	int maj;
127
128	aprint_naive("\n");
129	aprint_normal(": console\n");
130
131	sc->sc_dev = self;
132	sc->sc_tty = tty_alloc();
133	tty_attach(sc->sc_tty);
134	sc->sc_tty->t_oproc = ttycons_start;
135	sc->sc_tty->t_param = ttycons_param;
136	sc->sc_tty->t_sc = sc;
137
138	maj = cdevsw_lookup_major(&ttycons_cdevsw);
139	cn_tab->cn_dev = makedev(maj, device_unit(self));
140	sc->sc_tty->t_dev = cn_tab->cn_dev;
141
142	sc->sc_rd_sih = softint_establish(SOFTINT_SERIAL,
143	    ttycons_softintr, sc);
144	if (sc->sc_rd_sih == NULL)
145		panic("couldn't establish ttycons intr handler\n");
146
147	sc->sc_ctrlc_sih = softint_establish(SOFTINT_SERIAL,
148	    ttycons_softctrlc, sc);
149	if (sc->sc_ctrlc_sih == NULL)
150		panic("couldn't establish ttycons ctrlc handler\n");
151	sc->sc_ctrlz_sih = softint_establish(SOFTINT_SERIAL,
152	    ttycons_softctrlz, sc);
153	if (sc->sc_ctrlz_sih == NULL)
154		panic("couldn't establish ttycons ctrlz handler\n");
155
156	sigio_intr_establish(ttycons_intr, sc);
157	signal_intr_establish(SIGINT,  ttycons_ctrlc);
158	signal_intr_establish(SIGTSTP, ttycons_ctrlz);
159
160	if (thunk_set_stdin_sigio(true) != 0)
161		panic("couldn't enable stdin async mode");
162}
163
164void
165ttycons_consinit(void)
166{
167	struct thunk_termios t;
168
169	thunk_tcgetattr(0, &t);
170	t.c_lflag &= ~(ECHO|ICANON);
171	t.c_cc[VTIME] = 0;
172	t.c_cc[VMIN] = 1;
173	thunk_tcsetattr(0, TCSANOW, &t);
174
175	cn_tab = &ttycons_consdev;
176	cn_init_magic(&ttycons_cnm_state);
177	cn_set_magic("\047\001");
178}
179
180int
181ttycons_cngetc(dev_t dev)
182{
183	return thunk_getchar();
184}
185
186void
187ttycons_cnputc(dev_t dev, int c)
188{
189	thunk_putchar(c);
190}
191
192void
193ttycons_cnpollc(dev_t dev, int on)
194{
195}
196
197int
198ttycons_open(dev_t dev, int flag, int mode, lwp_t *l)
199{
200	struct ttycons_softc *sc;
201	struct tty *t;
202
203	sc = device_lookup_private(&ttycons_cd, minor(dev));
204	if (sc == NULL)
205		return ENXIO;
206	t = sc->sc_tty;
207
208	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, t))
209		return EBUSY;
210
211	if ((t->t_state & TS_ISOPEN) == 0 && t->t_wopen == 0) {
212		t->t_dev = dev;
213		ttychars(t);
214		t->t_iflag = TTYDEF_IFLAG;
215		t->t_oflag = TTYDEF_OFLAG;
216		t->t_cflag = TTYDEF_CFLAG;
217		t->t_lflag = TTYDEF_LFLAG;
218		t->t_ispeed = t->t_ospeed = TTYDEF_SPEED;
219		ttycons_param(t, &t->t_termios);
220		ttsetwater(t);
221	}
222	t->t_state |= TS_CARR_ON;
223
224	return t->t_linesw->l_open(dev, t);
225}
226
227int
228ttycons_close(dev_t dev, int flag, int mode, lwp_t *l)
229{
230	struct ttycons_softc *sc;
231	struct tty *t;
232
233	sc = device_lookup_private(&ttycons_cd, minor(dev));
234	t = sc->sc_tty;
235
236	if (t == NULL)
237		return 0;
238
239	t->t_linesw->l_close(t, flag);
240	ttyclose(t);
241
242	return 0;
243}
244
245int
246ttycons_read(dev_t dev, struct uio *uio, int flag)
247{
248	struct ttycons_softc *sc;
249	struct tty *t;
250
251	sc = device_lookup_private(&ttycons_cd, minor(dev));
252	t = sc->sc_tty;
253
254	return t->t_linesw->l_read(t, uio, flag);
255}
256
257int
258ttycons_write(dev_t dev, struct uio *uio, int flag)
259{
260	struct ttycons_softc *sc;
261	struct tty *t;
262
263	sc = device_lookup_private(&ttycons_cd, minor(dev));
264	t = sc->sc_tty;
265
266	return t->t_linesw->l_write(t, uio, flag);
267}
268
269int
270ttycons_poll(dev_t dev, int events, lwp_t *l)
271{
272	struct ttycons_softc *sc;
273	struct tty *t;
274
275	sc = device_lookup_private(&ttycons_cd, minor(dev));
276	t = sc->sc_tty;
277
278	return t->t_linesw->l_poll(t, events, l);
279}
280
281struct tty *
282ttycons_tty(dev_t dev)
283{
284	struct ttycons_softc *sc;
285
286	sc = device_lookup_private(&ttycons_cd, minor(dev));
287
288	return sc->sc_tty;
289}
290
291int
292ttycons_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
293{
294	struct ttycons_softc *sc;
295	struct tty *t;
296	int error;
297
298	sc = device_lookup_private(&ttycons_cd, minor(dev));
299	t = sc->sc_tty;
300
301	error = t->t_linesw->l_ioctl(t, cmd, data, flag, l);
302	if (error != EPASSTHROUGH)
303		return error;
304
305	error = ttioctl(t, cmd, data, flag, l);
306	if (error != EPASSTHROUGH)
307		return error;
308
309	return EPASSTHROUGH;
310}
311
312static void
313ttycons_start(struct tty *t)
314{
315	struct ttycons_softc *sc = t->t_sc;
316	u_char *p = sc->sc_buf;
317	int s, len, brem;
318
319	s = spltty();
320	if (t->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) {
321		splx(s);
322		return;
323	}
324	t->t_state |= TS_BUSY;
325	splx(s);
326
327	brem = q_to_b(&t->t_outq, sc->sc_buf, sizeof(sc->sc_buf));
328
329	while (brem > 0) {
330		len = thunk_write(1, p, brem);
331		if (len > 0) {
332			p += len;
333			brem -= len;
334		}
335	}
336
337	s = spltty();
338	t->t_state &= ~TS_BUSY;
339	if (ttypull(t)) {
340		t->t_state |= TS_TIMEOUT;
341		callout_schedule(&t->t_rstrt_ch, 1);
342	}
343	splx(s);
344}
345
346void
347ttycons_stop(struct tty *t, int flag)
348{
349}
350
351static int
352ttycons_param(struct tty *t, struct termios *c)
353{
354	t->t_ispeed = c->c_ispeed;
355	t->t_ospeed = c->c_ospeed;
356	t->t_cflag = c->c_cflag;
357	return 0;
358}
359
360static int
361ttycons_intr(void *priv)
362{
363	struct ttycons_softc *sc = priv;
364
365	softint_schedule(sc->sc_rd_sih);
366
367	return 0;
368}
369
370static void
371ttycons_softintr(void *priv)
372{
373	struct ttycons_softc *sc = priv;
374	struct tty *t = sc->sc_tty;
375	unsigned char ch;
376	int c;
377
378	while ((c = thunk_pollchar()) >= 0) {
379		ch = (unsigned char)c;
380		cn_check_magic(t->t_dev, ch, ttycons_cnm_state);
381		t->t_linesw->l_rint(ch, t);
382	}
383}
384
385
386/*
387 * handle SIGINT signal from trap.c
388 *
389 * argument 'pc' and 'va' are not used.
390 */
391static void
392ttycons_ctrlc(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
393{
394	struct ttycons_softc *sc;
395
396	sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev));
397	if (sc)
398		softint_schedule(sc->sc_ctrlc_sih);
399
400}
401
402static void
403ttycons_softctrlc(void *priv)
404{
405	struct ttycons_softc *sc = priv;
406	struct tty *t = sc->sc_tty;
407	unsigned char ch = 3;	/* ETX */
408
409	cn_check_magic(t->t_dev, ch, ttycons_cnm_state);
410	t->t_linesw->l_rint(ch, t);
411}
412
413/*
414 * handle SIGTSTP signal from trap.c
415 *
416 * argument 'pc' and 'va' are not used.
417 */
418static void
419ttycons_ctrlz(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
420{
421	struct ttycons_softc *sc;
422
423	sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev));
424	if (sc)
425		softint_schedule(sc->sc_ctrlz_sih);
426}
427
428static void
429ttycons_softctrlz(void *priv)
430{
431	struct ttycons_softc *sc = priv;
432	struct tty *t = sc->sc_tty;
433	unsigned char ch = 26;	/* SUB */
434
435	cn_check_magic(t->t_dev, ch, ttycons_cnm_state);
436	t->t_linesw->l_rint(ch, t);
437}
438