1 /* $OpenBSD: pdc.c,v 1.42 2024/05/22 14:25:47 jsg Exp $ */
2
3 /*
4 * Copyright (c) 1998-2003 Michael Shalayeff
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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "com.h"
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/device.h>
34 #include <sys/tty.h>
35 #include <sys/timeout.h>
36
37 #include <dev/cons.h>
38
39 #include <machine/conf.h>
40 #include <machine/pdc.h>
41 #include <machine/iomod.h>
42 #include <machine/autoconf.h>
43
44 typedef
45 struct pdc_softc {
46 struct device sc_dv;
47 struct tty *sc_tty;
48 struct timeout sc_to;
49 } pdcsoftc_t;
50
51 pdcio_t pdc;
52 int pdcret[32] PDC_ALIGNMENT;
53 char pdc_consbuf[IODC_MINIOSIZ] PDC_ALIGNMENT;
54 iodcio_t pdc_cniodc, pdc_kbdiodc;
55 pz_device_t *pz_kbd, *pz_cons;
56
57 struct consdev pdccons = { NULL, NULL, pdccngetc, pdccnputc,
58 nullcnpollc, NULL, makedev(22, 0), CN_LOWPRI };
59
60 int pdcmatch(struct device *, void *, void *);
61 void pdcattach(struct device *, struct device *, void *);
62
63 const struct cfattach pdc_ca = {
64 sizeof(pdcsoftc_t), pdcmatch, pdcattach
65 };
66
67 struct cfdriver pdc_cd = {
68 NULL, "pdc", DV_DULL
69 };
70
71 void pdcstart(struct tty *tp);
72 void pdctimeout(void *v);
73 int pdcparam(struct tty *tp, struct termios *);
74 int pdccnlookc(dev_t dev, int *cp);
75
76 #if NCOM > 0
77 /* serial console speed table */
78 static int pdc_speeds[] = {
79 B50,
80 B75,
81 B110,
82 B150,
83 B300,
84 B600,
85 B1200,
86 B2400,
87 B4800,
88 B7200,
89 B9600,
90 B19200,
91 B38400,
92 B57600,
93 B115200,
94 B230400,
95 };
96 #endif
97
98 void
pdc_init()99 pdc_init()
100 {
101 static int kbd_iodc[IODC_MAXSIZE/sizeof(int)];
102 static int cn_iodc[IODC_MAXSIZE/sizeof(int)];
103 int err;
104
105 /* XXX locore've done it XXX pdc = (pdcio_t)PAGE0->mem_pdc; */
106 pz_kbd = &PAGE0->mem_kbd;
107 pz_cons = &PAGE0->mem_cons;
108
109 /* XXX should we reset the console/kbd here?
110 well, /boot did that for us anyway */
111 if ((err = pdc_call((iodcio_t)pdc, 0, PDC_IODC, PDC_IODC_READ,
112 pdcret, pz_cons->pz_hpa, IODC_IO, cn_iodc, IODC_MAXSIZE)) < 0 ||
113 (err = pdc_call((iodcio_t)pdc, 0, PDC_IODC, PDC_IODC_READ,
114 pdcret, pz_kbd->pz_hpa, IODC_IO, kbd_iodc, IODC_MAXSIZE)) < 0) {
115 #ifdef DEBUG
116 printf("pdc_init: failed reading IODC (%d)\n", err);
117 #endif
118 }
119
120 pdc_cniodc = (iodcio_t)cn_iodc;
121 pdc_kbdiodc = (iodcio_t)kbd_iodc;
122
123 /* Start out with pdc as the console. */
124 cn_tab = &pdccons;
125
126 /* Figure out console settings. */
127 #if NCOM > 0
128 if (PAGE0->mem_cons.pz_class == PCL_DUPLEX) {
129 struct pz_device *pzd = &PAGE0->mem_cons;
130 extern int comdefaultrate;
131 #ifdef DEBUG
132 printf("console: class %d flags %b ",
133 pzd->pz_class, pzd->pz_flags, PZF_BITS);
134 printf("bc %d/%d/%d/%d/%d/%d ",
135 pzd->pz_bc[0], pzd->pz_bc[1], pzd->pz_bc[2],
136 pzd->pz_bc[3], pzd->pz_bc[4], pzd->pz_bc[5]);
137 printf("mod %x layers %x/%x/%x/%x/%x/%x hpa %x\n", pzd->pz_mod,
138 pzd->pz_layers[0], pzd->pz_layers[1], pzd->pz_layers[2],
139 pzd->pz_layers[3], pzd->pz_layers[4], pzd->pz_layers[5],
140 pzd->pz_hpa);
141
142 #endif
143
144 /* compute correct baud rate */
145 if (PZL_SPEED(pzd->pz_layers[0]) <
146 sizeof(pdc_speeds) / sizeof(int))
147 comdefaultrate =
148 pdc_speeds[PZL_SPEED(pzd->pz_layers[0])];
149 else
150 comdefaultrate = B9600; /* XXX */
151 }
152 #endif
153 }
154
155 int
pdcmatch(parent,cfdata,aux)156 pdcmatch(parent, cfdata, aux)
157 struct device *parent;
158 void *cfdata;
159 void *aux;
160 {
161 struct cfdata *cf = cfdata;
162 struct confargs *ca = aux;
163
164 /* there could be only one */
165 if (cf->cf_unit > 0 && !strcmp(ca->ca_name, "pdc"))
166 return 0;
167
168 return 1;
169 }
170
171 void
pdcattach(parent,self,aux)172 pdcattach(parent, self, aux)
173 struct device *parent;
174 struct device *self;
175 void *aux;
176 {
177 struct pdc_softc *sc = (struct pdc_softc *)self;
178
179 if (!pdc)
180 pdc_init();
181
182 printf("\n");
183
184 timeout_set(&sc->sc_to, pdctimeout, sc);
185 }
186
187 int
pdcopen(dev,flag,mode,p)188 pdcopen(dev, flag, mode, p)
189 dev_t dev;
190 int flag, mode;
191 struct proc *p;
192 {
193 int unit = minor(dev);
194 struct pdc_softc *sc;
195 struct tty *tp;
196 int s;
197 int error = 0, setuptimeout = 0;
198
199 if (unit >= pdc_cd.cd_ndevs || (sc = pdc_cd.cd_devs[unit]) == NULL)
200 return ENXIO;
201
202 s = spltty();
203
204 if (sc->sc_tty)
205 tp = sc->sc_tty;
206 else {
207 tp = sc->sc_tty = ttymalloc(0);
208 }
209
210 tp->t_oproc = pdcstart;
211 tp->t_param = pdcparam;
212 tp->t_dev = dev;
213 if ((tp->t_state & TS_ISOPEN) == 0) {
214 ttychars(tp);
215 tp->t_iflag = TTYDEF_IFLAG;
216 tp->t_oflag = TTYDEF_OFLAG;
217 tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
218 tp->t_lflag = TTYDEF_LFLAG;
219 tp->t_ispeed = tp->t_ospeed = B9600;
220 ttsetwater(tp);
221
222 setuptimeout = 1;
223 } else if (tp->t_state&TS_XCLUDE && suser(p) != 0) {
224 splx(s);
225 return (EBUSY);
226 }
227 tp->t_state |= TS_CARR_ON;
228 splx(s);
229
230 error = (*linesw[tp->t_line].l_open)(dev, tp, p);
231 if (error == 0 && setuptimeout)
232 pdctimeout(sc);
233
234 return error;
235 }
236
237 int
pdcclose(dev,flag,mode,p)238 pdcclose(dev, flag, mode, p)
239 dev_t dev;
240 int flag, mode;
241 struct proc *p;
242 {
243 int unit = minor(dev);
244 struct tty *tp;
245 struct pdc_softc *sc;
246
247 if (unit >= pdc_cd.cd_ndevs || (sc = pdc_cd.cd_devs[unit]) == NULL)
248 return ENXIO;
249
250 tp = sc->sc_tty;
251 timeout_del(&sc->sc_to);
252 (*linesw[tp->t_line].l_close)(tp, flag, p);
253 ttyclose(tp);
254 return 0;
255 }
256
257 int
pdcread(dev,uio,flag)258 pdcread(dev, uio, flag)
259 dev_t dev;
260 struct uio *uio;
261 int flag;
262 {
263 int unit = minor(dev);
264 struct tty *tp;
265 struct pdc_softc *sc;
266
267 if (unit >= pdc_cd.cd_ndevs || (sc = pdc_cd.cd_devs[unit]) == NULL)
268 return ENXIO;
269
270 tp = sc->sc_tty;
271 return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
272 }
273
274 int
pdcwrite(dev,uio,flag)275 pdcwrite(dev, uio, flag)
276 dev_t dev;
277 struct uio *uio;
278 int flag;
279 {
280 int unit = minor(dev);
281 struct tty *tp;
282 struct pdc_softc *sc;
283
284 if (unit >= pdc_cd.cd_ndevs || (sc = pdc_cd.cd_devs[unit]) == NULL)
285 return ENXIO;
286
287 tp = sc->sc_tty;
288 return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
289 }
290
291 int
pdcioctl(dev,cmd,data,flag,p)292 pdcioctl(dev, cmd, data, flag, p)
293 dev_t dev;
294 u_long cmd;
295 caddr_t data;
296 int flag;
297 struct proc *p;
298 {
299 int unit = minor(dev);
300 int error;
301 struct tty *tp;
302 struct pdc_softc *sc;
303
304 if (unit >= pdc_cd.cd_ndevs || (sc = pdc_cd.cd_devs[unit]) == NULL)
305 return ENXIO;
306
307 tp = sc->sc_tty;
308 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
309 if (error >= 0)
310 return error;
311 error = ttioctl(tp, cmd, data, flag, p);
312 if (error >= 0)
313 return error;
314
315 return ENOTTY;
316 }
317
318 int
pdcparam(tp,t)319 pdcparam(tp, t)
320 struct tty *tp;
321 struct termios *t;
322 {
323
324 return 0;
325 }
326
327 void
pdcstart(tp)328 pdcstart(tp)
329 struct tty *tp;
330 {
331 int s;
332
333 s = spltty();
334 if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
335 splx(s);
336 return;
337 }
338 ttwakeupwr(tp);
339 tp->t_state |= TS_BUSY;
340 while (tp->t_outq.c_cc != 0)
341 pdccnputc(tp->t_dev, getc(&tp->t_outq));
342 tp->t_state &= ~TS_BUSY;
343 splx(s);
344 }
345
346 int
pdcstop(tp,flag)347 pdcstop(tp, flag)
348 struct tty *tp;
349 int flag;
350 {
351 int s;
352
353 s = spltty();
354 if (tp->t_state & TS_BUSY)
355 if ((tp->t_state & TS_TTSTOP) == 0)
356 tp->t_state |= TS_FLUSH;
357 splx(s);
358 return 0;
359 }
360
361 void
pdctimeout(v)362 pdctimeout(v)
363 void *v;
364 {
365 struct pdc_softc *sc = v;
366 struct tty *tp = sc->sc_tty;
367 int c;
368
369 while (pdccnlookc(tp->t_dev, &c)) {
370 if (tp->t_state & TS_ISOPEN)
371 (*linesw[tp->t_line].l_rint)(c, tp);
372 }
373 timeout_add(&sc->sc_to, 1);
374 }
375
376 struct tty *
pdctty(dev)377 pdctty(dev)
378 dev_t dev;
379 {
380 int unit = minor(dev);
381 struct pdc_softc *sc;
382
383 if (unit >= pdc_cd.cd_ndevs || (sc = pdc_cd.cd_devs[unit]) == NULL)
384 return NULL;
385
386 return sc->sc_tty;
387 }
388
389 int
pdccnlookc(dev,cp)390 pdccnlookc(dev, cp)
391 dev_t dev;
392 int *cp;
393 {
394 int err, l;
395 int s = splhigh();
396
397 err = pdc_call(pdc_kbdiodc, 0, pz_kbd->pz_hpa, IODC_IO_CONSIN,
398 pz_kbd->pz_spa, pz_kbd->pz_layers, pdcret, 0, pdc_consbuf, 1, 0);
399
400 l = pdcret[0];
401 *cp = pdc_consbuf[0];
402 splx(s);
403 #ifdef DEBUG
404 if (err < 0)
405 printf("pdccnlookc: input error: %d\n", err);
406 #endif
407
408 return l;
409 }
410
411 int
pdccngetc(dev)412 pdccngetc(dev)
413 dev_t dev;
414 {
415 int c;
416
417 if (!pdc)
418 return 0;
419
420 while(!pdccnlookc(dev, &c))
421 ;
422
423 return (c);
424 }
425
426 void
pdccnputc(dev,c)427 pdccnputc(dev, c)
428 dev_t dev;
429 int c;
430 {
431 register int err;
432 int s = splhigh();
433
434 *pdc_consbuf = c;
435 err = pdc_call(pdc_cniodc, 0, pz_cons->pz_hpa, IODC_IO_CONSOUT,
436 pz_cons->pz_spa, pz_cons->pz_layers, pdcret, 0, pdc_consbuf, 1, 0);
437 splx(s);
438
439 if (err < 0) {
440 #ifdef DEBUG
441 printf("pdccnputc: output error: %d\n", err);
442 #endif
443 }
444 }
445