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