1 /* $OpenBSD: vcctty.c,v 1.15 2021/10/24 17:05:04 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 const 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
vcctty_match(struct device * parent,void * match,void * aux)93 vcctty_match(struct device *parent, void *match, void *aux)
94 {
95 return (1);
96 }
97
98 void
vcctty_attach(struct device * parent,struct device * self,void * aux)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
vcctty_tx_intr(void * arg)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
vcctty_rx_intr(void * arg)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
vcctty_send_data(struct vcctty_softc * sc,struct tty * tp)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
vcctty_send_break(struct vcctty_softc * sc)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
vccttyopen(dev_t dev,int flag,int mode,struct proc * p)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
vccttyclose(dev_t dev,int flag,int mode,struct proc * p)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
vccttyread(dev_t dev,struct uio * uio,int flag)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
vccttywrite(dev_t dev,struct uio * uio,int flag)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
vccttyioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)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
vccttystart(struct tty * tp)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
vccttystop(struct tty * tp,int flag)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 *
vccttytty(dev_t dev)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
vccttyparam(struct tty * tp,struct termios * t)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
vccttyhwiflow(struct tty * tp,int stop)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