xref: /openbsd-src/sys/arch/sparc64/dev/vcctty.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: vcctty.c,v 1.6 2010/07/02 17:27:01 nicm Exp $	*/
2 /*
3  * Copyright (c) 2009 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/proc.h>
23 #include <sys/systm.h>
24 #include <sys/tty.h>
25 
26 #include <machine/autoconf.h>
27 #include <machine/conf.h>
28 #include <machine/hypervisor.h>
29 #include <machine/mdesc.h>
30 
31 #include <dev/cons.h>
32 #include <sparc64/dev/cbusvar.h>
33 #include <sparc64/dev/ldcvar.h>
34 
35 #ifdef VCCTTY_DEBUG
36 #define DPRINTF(x)	printf x
37 #else
38 #define DPRINTF(x)
39 #endif
40 
41 #define VCCTTY_TX_ENTRIES	128
42 #define VCCTTY_RX_ENTRIES	128
43 
44 struct vcctty_msg {
45 	uint8_t		type;
46 	uint8_t		size;
47 	uint16_t	rsvd;
48 	uint32_t	ctrl_msg;
49 	uint8_t		data[56];
50 };
51 
52 /* Packet types. */
53 #define LDC_CONSOLE_CTRL	0x01
54 #define LDC_CONSOLE_DATA	0x02
55 
56 struct vcctty_softc {
57 	struct device	sc_dv;
58 	bus_space_tag_t	sc_bustag;
59 	bus_dma_tag_t	sc_dmatag;
60 
61 	void		*sc_tx_ih;
62 	void		*sc_rx_ih;
63 	uint64_t	sc_tx_sysino;
64 	uint64_t	sc_rx_sysino;
65 
66 	struct ldc_conn	sc_lc;
67 
68 	struct tty	*sc_tty;
69 };
70 
71 int	vcctty_match(struct device *, void *, void *);
72 void	vcctty_attach(struct device *, struct device *, void *);
73 
74 struct cfattach vcctty_ca = {
75 	sizeof(struct vcctty_softc), vcctty_match, vcctty_attach
76 };
77 
78 struct cfdriver vcctty_cd = {
79 	NULL, "vcctty", DV_DULL
80 };
81 
82 int	vcctty_tx_intr(void *);
83 int	vcctty_rx_intr(void *);
84 
85 void	vcctty_send_data(struct vcctty_softc *, int);
86 void	vcctty_send_break(struct vcctty_softc *);
87 
88 void	vccttystart(struct tty *);
89 int	vccttyparam(struct tty *, struct termios *);
90 
91 int
92 vcctty_match(struct device *parent, void *match, void *aux)
93 {
94 	return (1);
95 }
96 
97 void
98 vcctty_attach(struct device *parent, struct device *self, void *aux)
99 {
100 	struct vcctty_softc *sc = (struct vcctty_softc *)self;
101 	struct cbus_attach_args *ca = aux;
102 	struct ldc_conn *lc;
103 	int err;
104 
105 	sc->sc_bustag = ca->ca_bustag;
106 	sc->sc_dmatag = ca->ca_dmatag;
107 
108 	if (cbus_intr_map(ca->ca_node, ca->ca_tx_ino, &sc->sc_tx_sysino) ||
109 	    cbus_intr_map(ca->ca_node, ca->ca_rx_ino, &sc->sc_rx_sysino)) {
110 		printf(": can't map interrupt\n");
111 		return;
112 	}
113 	printf(": ivec 0x%lx, 0x%lx", sc->sc_tx_sysino, sc->sc_rx_sysino);
114 
115 	/*
116 	 * Un-configure queues before registering interrupt handlers,
117 	 * such that we dont get any stale LDC packets or events.
118 	 */
119 	hv_ldc_tx_qconf(ca->ca_id, 0, 0);
120 	hv_ldc_rx_qconf(ca->ca_id, 0, 0);
121 
122 	sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_sysino,
123 	    IPL_TTY, 0, vcctty_tx_intr, sc, sc->sc_dv.dv_xname);
124 	sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_sysino,
125 	    IPL_TTY, 0, vcctty_rx_intr, sc, sc->sc_dv.dv_xname);
126 	if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
127 		printf(", can't establish interrupt\n");
128 		return;
129 	}
130 
131 	lc = &sc->sc_lc;
132 	lc->lc_id = ca->ca_id;
133 	lc->lc_sc = sc;
134 
135 	lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_TX_ENTRIES);
136 	if (lc->lc_txq == NULL) {
137 		printf(", can't allocate tx queue\n");
138 		return;
139 	}
140 
141 	lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_RX_ENTRIES);
142 	if (lc->lc_rxq == NULL) {
143 		printf(", can't allocate rx queue\n");
144 		goto free_txqueue;
145 	}
146 
147 	err = hv_ldc_tx_qconf(lc->lc_id,
148 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
149 	if (err != H_EOK)
150 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
151 
152 	err = hv_ldc_rx_qconf(lc->lc_id,
153 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
154 	if (err != H_EOK)
155 		printf("%d: hv_ldc_rx_qconf %d\n", __func__, err);
156 
157 	printf(" domain \"%s\"\n", ca->ca_name);
158 	return;
159 
160 free_txqueue:
161 	ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
162 }
163 
164 int
165 vcctty_tx_intr(void *arg)
166 {
167 	struct vcctty_softc *sc = arg;
168 	struct ldc_conn *lc = &sc->sc_lc;
169 	uint64_t tx_head, tx_tail, tx_state;
170 	int err;
171 
172 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
173 	if (err != H_EOK) {
174 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
175 		return (0);
176 	}
177 
178 	if (tx_state != lc->lc_tx_state) {
179 		switch (tx_state) {
180 		case LDC_CHANNEL_DOWN:
181 			DPRINTF(("Tx link down\n"));
182 			break;
183 		case LDC_CHANNEL_UP:
184 			DPRINTF(("Tx link up\n"));
185 			break;
186 		case LDC_CHANNEL_RESET:
187 			DPRINTF(("Tx link reset\n"));
188 			break;
189 		}
190 		lc->lc_tx_state = tx_state;
191 	}
192 
193 	return (1);
194 }
195 
196 int
197 vcctty_rx_intr(void *arg)
198 {
199 	struct vcctty_softc *sc = arg;
200 	struct tty *tp = sc->sc_tty;
201 	struct ldc_conn *lc = &sc->sc_lc;
202 	uint64_t rx_head, rx_tail, rx_state;
203 	struct vcctty_msg *msg;
204 	int err;
205 	int i;
206 
207 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
208 	if (err != H_EOK) {
209 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
210 		return (0);
211 	}
212 
213 	if (rx_state != lc->lc_rx_state) {
214 		switch (rx_state) {
215 		case LDC_CHANNEL_DOWN:
216 			DPRINTF(("Rx link down\n"));
217 			break;
218 		case LDC_CHANNEL_UP:
219 			DPRINTF(("Rx link up\n"));
220 			break;
221 		case LDC_CHANNEL_RESET:
222 			DPRINTF(("Rx link reset\n"));
223 			break;
224 		}
225 		lc->lc_rx_state = rx_state;
226 		return (1);
227 	}
228 
229 	if (rx_head == rx_tail)
230 		return (0);
231 
232 	msg = (struct vcctty_msg *)(lc->lc_rxq->lq_va + rx_head);
233 	if (tp && msg->type == LDC_CONSOLE_DATA) {
234 		for (i = 0; i < msg->size; i++) {
235 			if (tp->t_state & TS_ISOPEN)
236 				(*linesw[tp->t_line].l_rint)(msg->data[i], tp);
237 		}
238 	}
239 
240 	rx_head += sizeof(*msg);
241 	rx_head &= ((lc->lc_rxq->lq_nentries * sizeof(*msg)) - 1);
242 	err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
243 	if (err != H_EOK)
244 		printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
245 
246 	return (1);
247 }
248 
249 void
250 vcctty_send_data(struct vcctty_softc *sc, int c)
251 {
252 	struct ldc_conn *lc = &sc->sc_lc;
253 	uint64_t tx_head, tx_tail, tx_state;
254 	struct vcctty_msg *msg;
255 	int err;
256 
257 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
258 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
259 		return;
260 
261 	msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail);
262 	bzero(msg, sizeof(*msg));
263 	msg->type = LDC_CONSOLE_DATA;
264 	msg->size = 1;
265 	msg->data[0] = c;
266 
267 	tx_tail += sizeof(*msg);
268 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1);
269 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
270 	if (err != H_EOK)
271 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
272 }
273 
274 void
275 vcctty_send_break(struct vcctty_softc *sc)
276 {
277 	struct ldc_conn *lc = &sc->sc_lc;
278 	uint64_t tx_head, tx_tail, tx_state;
279 	struct vcctty_msg *msg;
280 	int err;
281 
282 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
283 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
284 		return;
285 
286 	msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail);
287 	bzero(msg, sizeof(*msg));
288 	msg->type = LDC_CONSOLE_CTRL;
289 	msg->ctrl_msg = CONS_BREAK;
290 
291 	tx_tail += sizeof(*msg);
292 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1);
293 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
294 	if (err != H_EOK)
295 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
296 }
297 
298 int
299 vccttyopen(dev_t dev, int flag, int mode, struct proc *p)
300 {
301 	struct vcctty_softc *sc;
302 	struct tty *tp;
303 	int unit = minor(dev);
304 
305 	if (unit > vcctty_cd.cd_ndevs)
306 		return (ENXIO);
307 	sc = vcctty_cd.cd_devs[unit];
308 	if (sc == NULL)
309 		return (ENXIO);
310 
311 	if (sc->sc_tty)
312 		tp = sc->sc_tty;
313 	else
314 		tp = sc->sc_tty = ttymalloc(0);
315 
316 	tp->t_oproc = vccttystart;
317 	tp->t_param = vccttyparam;
318 	tp->t_dev = dev;
319 	if ((tp->t_state & TS_ISOPEN) == 0) {
320 		ttychars(tp);
321 		tp->t_iflag = TTYDEF_IFLAG;
322 		tp->t_oflag = TTYDEF_OFLAG;
323 		tp->t_cflag = TTYDEF_CFLAG;
324 		tp->t_lflag = TTYDEF_LFLAG;
325 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
326 		ttsetwater(tp);
327 	} else if ((tp->t_state & TS_XCLUDE) && suser(p, 0))
328 		return (EBUSY);
329 	tp->t_state |= TS_CARR_ON;
330 
331 	return ((*linesw[tp->t_line].l_open)(dev, tp, p));
332 }
333 
334 int
335 vccttyclose(dev_t dev, int flag, int mode, struct proc *p)
336 {
337 	struct vcctty_softc *sc;
338 	struct tty *tp;
339 	int unit = minor(dev);
340 
341 	if (unit > vcctty_cd.cd_ndevs)
342 		return (ENXIO);
343 	sc = vcctty_cd.cd_devs[unit];
344 	if (sc == NULL)
345 		return (ENXIO);
346 
347 	tp = sc->sc_tty;
348 	(*linesw[tp->t_line].l_close)(tp, flag, p);
349 	ttyclose(tp);
350 	return (0);
351 }
352 
353 int
354 vccttyread(dev_t dev, struct uio *uio, int flag)
355 {
356 	struct vcctty_softc *sc;
357 	struct tty *tp;
358 	int unit = minor(dev);
359 
360 	if (unit > vcctty_cd.cd_ndevs)
361 		return (ENXIO);
362 	sc = vcctty_cd.cd_devs[unit];
363 	if (sc == NULL)
364 		return (ENXIO);
365 
366 	tp = sc->sc_tty;
367 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
368 }
369 
370 int
371 vccttywrite(dev_t dev, struct uio *uio, int flag)
372 {
373 	struct vcctty_softc *sc;
374 	struct tty *tp;
375 	int unit = minor(dev);
376 
377 	if (unit > vcctty_cd.cd_ndevs)
378 		return (ENXIO);
379 	sc = vcctty_cd.cd_devs[unit];
380 	if (sc == NULL)
381 		return (ENXIO);
382 
383 	tp = sc->sc_tty;
384 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
385 }
386 
387 int
388 vccttyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
389 {
390 	struct vcctty_softc *sc;
391 	struct tty *tp;
392 	int unit = minor(dev);
393 	int error;
394 
395 	if (unit > vcctty_cd.cd_ndevs)
396 		return (ENXIO);
397 	sc = vcctty_cd.cd_devs[unit];
398 	if (sc == NULL)
399 		return (ENXIO);
400 
401 	tp = sc->sc_tty;
402 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
403 	if (error >= 0)
404 		return error;
405 	error = ttioctl(tp, cmd, data, flag, p);
406 	if (error >= 0)
407 		return (error);
408 
409 	error = 0;
410 
411 	switch (cmd) {
412 	case TIOCSBRK:
413 		vcctty_send_break(sc);
414 		break;
415 	case TIOCCBRK:
416 		/* BREAK gets cleared automatically. */
417 		break;
418 	default:
419 		error = ENOTTY;
420 		break;
421 	}
422 
423 	return (error);
424 }
425 
426 void
427 vccttystart(struct tty *tp)
428 {
429 	struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)];
430 	int s;
431 
432 	s = spltty();
433 	if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
434 		splx(s);
435 		return;
436 	}
437 	ttwakeupwr(tp);
438 	tp->t_state |= TS_BUSY;
439 	while (tp->t_outq.c_cc != 0)
440 		vcctty_send_data(sc, getc(&tp->t_outq));
441 	tp->t_state &= ~TS_BUSY;
442 	splx(s);
443 }
444 
445 int
446 vccttystop(struct tty *tp, int flag)
447 {
448 	int s;
449 
450 	s = spltty();
451 	if (tp->t_state & TS_BUSY)
452 		if ((tp->t_state & TS_TTSTOP) == 0)
453 			tp->t_state |= TS_FLUSH;
454 	splx(s);
455 	return (0);
456 }
457 
458 struct tty *
459 vccttytty(dev_t dev)
460 {
461 	struct vcctty_softc *sc;
462 	int unit = minor(dev);
463 
464 	if (unit > vcctty_cd.cd_ndevs)
465 		return (NULL);
466 	sc = vcctty_cd.cd_devs[unit];
467 	if (sc == NULL)
468 		return (NULL);
469 
470 	return sc->sc_tty;
471 }
472 
473 int
474 vccttyparam(struct tty *tp, struct termios *t)
475 {
476 	tp->t_ispeed = t->c_ispeed;
477 	tp->t_ospeed = t->c_ospeed;
478 	tp->t_cflag = t->c_cflag;
479 	return (0);
480 }
481