xref: /openbsd/sys/arch/sparc64/dev/vcctty.c (revision 4cfece93)
1 /*	$OpenBSD: vcctty.c,v 1.14 2018/02/19 08:59:52 mpi 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 	uint64_t	sc_tx_ino;
62 	uint64_t	sc_rx_ino;
63 	void		*sc_tx_ih;
64 	void		*sc_rx_ih;
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 *, struct tty *);
86 void	vcctty_send_break(struct vcctty_softc *);
87 
88 void	vccttystart(struct tty *);
89 int	vccttyparam(struct tty *, struct termios *);
90 int	vccttyhwiflow(struct tty *, int);
91 
92 int
93 vcctty_match(struct device *parent, void *match, void *aux)
94 {
95 	return (1);
96 }
97 
98 void
99 vcctty_attach(struct device *parent, struct device *self, void *aux)
100 {
101 	struct vcctty_softc *sc = (struct vcctty_softc *)self;
102 	struct cbus_attach_args *ca = aux;
103 	struct ldc_conn *lc;
104 	int err;
105 
106 	sc->sc_bustag = ca->ca_bustag;
107 	sc->sc_dmatag = ca->ca_dmatag;
108 	sc->sc_tx_ino = ca->ca_tx_ino;
109 	sc->sc_rx_ino = ca->ca_rx_ino;
110 
111 	printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_ino, sc->sc_rx_ino);
112 
113 	/*
114 	 * Un-configure queues before registering interrupt handlers,
115 	 * such that we dont get any stale LDC packets or events.
116 	 */
117 	hv_ldc_tx_qconf(ca->ca_id, 0, 0);
118 	hv_ldc_rx_qconf(ca->ca_id, 0, 0);
119 
120 	sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_ino,
121 	    IPL_TTY, 0, vcctty_tx_intr, sc, sc->sc_dv.dv_xname);
122 	sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_ino,
123 	    IPL_TTY, 0, vcctty_rx_intr, sc, sc->sc_dv.dv_xname);
124 	if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
125 		printf(", can't establish interrupt\n");
126 		return;
127 	}
128 
129 	lc = &sc->sc_lc;
130 	lc->lc_id = ca->ca_id;
131 	lc->lc_sc = sc;
132 
133 	lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_TX_ENTRIES);
134 	if (lc->lc_txq == NULL) {
135 		printf(", can't allocate tx queue\n");
136 		return;
137 	}
138 
139 	lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_RX_ENTRIES);
140 	if (lc->lc_rxq == NULL) {
141 		printf(", can't allocate rx queue\n");
142 		goto free_txqueue;
143 	}
144 
145 	err = hv_ldc_tx_qconf(lc->lc_id,
146 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
147 	if (err != H_EOK)
148 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
149 
150 	err = hv_ldc_rx_qconf(lc->lc_id,
151 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
152 	if (err != H_EOK)
153 		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
154 
155 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_ENABLED);
156 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_ENABLED);
157 
158 	printf(" domain \"%s\"\n", ca->ca_name);
159 	return;
160 
161 free_txqueue:
162 	ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
163 }
164 
165 int
166 vcctty_tx_intr(void *arg)
167 {
168 	struct vcctty_softc *sc = arg;
169 	struct ldc_conn *lc = &sc->sc_lc;
170 	uint64_t tx_head, tx_tail, tx_state;
171 	int err;
172 
173 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
174 	if (err != H_EOK) {
175 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
176 		return (0);
177 	}
178 
179 	if (tx_state != lc->lc_tx_state) {
180 		switch (tx_state) {
181 		case LDC_CHANNEL_DOWN:
182 			DPRINTF(("%s: Tx link down\n", __func__));
183 			break;
184 		case LDC_CHANNEL_UP:
185 			DPRINTF(("%s: Tx link up\n", __func__));
186 			break;
187 		case LDC_CHANNEL_RESET:
188 			DPRINTF(("%s: Tx link reset\n", __func__));
189 			break;
190 		}
191 		lc->lc_tx_state = tx_state;
192 	}
193 
194 	return (1);
195 }
196 
197 int
198 vcctty_rx_intr(void *arg)
199 {
200 	struct vcctty_softc *sc = arg;
201 	struct tty *tp = sc->sc_tty;
202 	struct ldc_conn *lc = &sc->sc_lc;
203 	uint64_t rx_head, rx_tail, rx_state;
204 	struct vcctty_msg *msg;
205 	int err;
206 	int i;
207 
208 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
209 	if (err != H_EOK) {
210 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
211 		return (0);
212 	}
213 
214 	if (rx_state != lc->lc_rx_state) {
215 		switch (rx_state) {
216 		case LDC_CHANNEL_DOWN:
217 			DPRINTF(("%s: Rx link down\n", __func__));
218 			break;
219 		case LDC_CHANNEL_UP:
220 			DPRINTF(("%s: Rx link up\n", __func__));
221 			break;
222 		case LDC_CHANNEL_RESET:
223 			DPRINTF(("%s: Rx link reset\n", __func__));
224 			break;
225 		}
226 		lc->lc_rx_state = rx_state;
227 		return (1);
228 	}
229 
230 	if (rx_head == rx_tail)
231 		return (0);
232 
233 	msg = (struct vcctty_msg *)(lc->lc_rxq->lq_va + rx_head);
234 	if (tp && msg->type == LDC_CONSOLE_DATA) {
235 		for (i = 0; i < msg->size; i++) {
236 			if (tp->t_state & TS_ISOPEN)
237 				(*linesw[tp->t_line].l_rint)(msg->data[i], tp);
238 		}
239 	}
240 
241 	rx_head += sizeof(*msg);
242 	rx_head &= ((lc->lc_rxq->lq_nentries * sizeof(*msg)) - 1);
243 	err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
244 	if (err != H_EOK)
245 		printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
246 
247 	return (1);
248 }
249 
250 void
251 vcctty_send_data(struct vcctty_softc *sc, struct tty *tp)
252 {
253 	struct ldc_conn *lc = &sc->sc_lc;
254 	uint64_t tx_head, tx_tail, tx_state;
255 	uint64_t next_tx_tail;
256 	struct vcctty_msg *msg;
257 	int err;
258 
259 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
260 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
261 		return;
262 
263 	while (tp->t_outq.c_cc > 0) {
264 		next_tx_tail = tx_tail + sizeof(*msg);
265 		next_tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1);
266 
267 		if (next_tx_tail == tx_head)
268 			return;
269 
270 		msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail);
271 		bzero(msg, sizeof(*msg));
272 		msg->type = LDC_CONSOLE_DATA;
273 		msg->size = q_to_b(&tp->t_outq, msg->data, sizeof(msg->data));
274 
275 		err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail);
276 		if (err != H_EOK)
277 			printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
278 		tx_tail = next_tx_tail;
279 	}
280 }
281 
282 void
283 vcctty_send_break(struct vcctty_softc *sc)
284 {
285 	struct ldc_conn *lc = &sc->sc_lc;
286 	uint64_t tx_head, tx_tail, tx_state;
287 	struct vcctty_msg *msg;
288 	int err;
289 
290 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
291 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
292 		return;
293 
294 	msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail);
295 	bzero(msg, sizeof(*msg));
296 	msg->type = LDC_CONSOLE_CTRL;
297 	msg->ctrl_msg = CONS_BREAK;
298 
299 	tx_tail += sizeof(*msg);
300 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1);
301 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
302 	if (err != H_EOK)
303 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
304 }
305 
306 int
307 vccttyopen(dev_t dev, int flag, int mode, struct proc *p)
308 {
309 	struct vcctty_softc *sc;
310 	struct tty *tp;
311 	int unit = minor(dev);
312 
313 	if (unit >= vcctty_cd.cd_ndevs)
314 		return (ENXIO);
315 	sc = vcctty_cd.cd_devs[unit];
316 	if (sc == NULL)
317 		return (ENXIO);
318 
319 	if (sc->sc_tty)
320 		tp = sc->sc_tty;
321 	else
322 		tp = sc->sc_tty = ttymalloc(0);
323 
324 	tp->t_oproc = vccttystart;
325 	tp->t_param = vccttyparam;
326 	tp->t_hwiflow = vccttyhwiflow;
327 	tp->t_dev = dev;
328 	if ((tp->t_state & TS_ISOPEN) == 0) {
329 		ttychars(tp);
330 		tp->t_iflag = TTYDEF_IFLAG;
331 		tp->t_oflag = TTYDEF_OFLAG;
332 		tp->t_cflag = TTYDEF_CFLAG | CRTSCTS;
333 		tp->t_lflag = TTYDEF_LFLAG;
334 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
335 		ttsetwater(tp);
336 	} else if ((tp->t_state & TS_XCLUDE) && suser(p))
337 		return (EBUSY);
338 	tp->t_state |= TS_CARR_ON;
339 
340 	return ((*linesw[tp->t_line].l_open)(dev, tp, p));
341 }
342 
343 int
344 vccttyclose(dev_t dev, int flag, int mode, struct proc *p)
345 {
346 	struct vcctty_softc *sc;
347 	struct tty *tp;
348 	int unit = minor(dev);
349 
350 	if (unit >= vcctty_cd.cd_ndevs)
351 		return (ENXIO);
352 	sc = vcctty_cd.cd_devs[unit];
353 	if (sc == NULL)
354 		return (ENXIO);
355 
356 	tp = sc->sc_tty;
357 	(*linesw[tp->t_line].l_close)(tp, flag, p);
358 	ttyclose(tp);
359 	return (0);
360 }
361 
362 int
363 vccttyread(dev_t dev, struct uio *uio, int flag)
364 {
365 	struct vcctty_softc *sc;
366 	struct tty *tp;
367 	int unit = minor(dev);
368 
369 	if (unit >= vcctty_cd.cd_ndevs)
370 		return (ENXIO);
371 	sc = vcctty_cd.cd_devs[unit];
372 	if (sc == NULL)
373 		return (ENXIO);
374 
375 	tp = sc->sc_tty;
376 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
377 }
378 
379 int
380 vccttywrite(dev_t dev, struct uio *uio, int flag)
381 {
382 	struct vcctty_softc *sc;
383 	struct tty *tp;
384 	int unit = minor(dev);
385 
386 	if (unit >= vcctty_cd.cd_ndevs)
387 		return (ENXIO);
388 	sc = vcctty_cd.cd_devs[unit];
389 	if (sc == NULL)
390 		return (ENXIO);
391 
392 	tp = sc->sc_tty;
393 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
394 }
395 
396 int
397 vccttyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
398 {
399 	struct vcctty_softc *sc;
400 	struct tty *tp;
401 	int unit = minor(dev);
402 	int error;
403 
404 	if (unit >= vcctty_cd.cd_ndevs)
405 		return (ENXIO);
406 	sc = vcctty_cd.cd_devs[unit];
407 	if (sc == NULL)
408 		return (ENXIO);
409 
410 	tp = sc->sc_tty;
411 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
412 	if (error >= 0)
413 		return error;
414 	error = ttioctl(tp, cmd, data, flag, p);
415 	if (error >= 0)
416 		return (error);
417 
418 	error = 0;
419 
420 	switch (cmd) {
421 	case TIOCSBRK:
422 		vcctty_send_break(sc);
423 		break;
424 	case TIOCCBRK:
425 		/* BREAK gets cleared automatically. */
426 		break;
427 	default:
428 		error = ENOTTY;
429 		break;
430 	}
431 
432 	return (error);
433 }
434 
435 void
436 vccttystart(struct tty *tp)
437 {
438 	struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)];
439 	int s;
440 
441 	s = spltty();
442 	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
443 		splx(s);
444 		return;
445 	}
446 	ttwakeupwr(tp);
447 	tp->t_state |= TS_BUSY;
448 	if (tp->t_outq.c_cc > 0)
449 		vcctty_send_data(sc, tp);
450 	tp->t_state &= ~TS_BUSY;
451 	if (tp->t_outq.c_cc > 0) {
452 		tp->t_state |= TS_TIMEOUT;
453 		timeout_add(&tp->t_rstrt_to, 1);
454 	}
455 	splx(s);
456 }
457 
458 int
459 vccttystop(struct tty *tp, int flag)
460 {
461 	int s;
462 
463 	s = spltty();
464 	if (tp->t_state & TS_BUSY)
465 		if ((tp->t_state & TS_TTSTOP) == 0)
466 			tp->t_state |= TS_FLUSH;
467 	splx(s);
468 	return (0);
469 }
470 
471 struct tty *
472 vccttytty(dev_t dev)
473 {
474 	struct vcctty_softc *sc;
475 	int unit = minor(dev);
476 
477 	if (unit >= vcctty_cd.cd_ndevs)
478 		return (NULL);
479 	sc = vcctty_cd.cd_devs[unit];
480 	if (sc == NULL)
481 		return (NULL);
482 
483 	return sc->sc_tty;
484 }
485 
486 int
487 vccttyparam(struct tty *tp, struct termios *t)
488 {
489 	tp->t_ispeed = t->c_ispeed;
490 	tp->t_ospeed = t->c_ospeed;
491 	tp->t_cflag = t->c_cflag;
492 	return (0);
493 }
494 
495 int
496 vccttyhwiflow(struct tty *tp, int stop)
497 {
498 	struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)];
499 	uint64_t state = stop ? INTR_DISABLED : INTR_ENABLED;
500 
501 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, state);
502 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, state);
503 
504 	return (1);
505 }
506