grf.c revision 1.4 1 /* $NetBSD: grf.c,v 1.4 1996/10/13 03:34:47 christos Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1990, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * from: Utah $Hdr: grf.c 1.36 93/08/13$
41 *
42 * @(#)grf.c 8.4 (Berkeley) 1/12/94
43 */
44
45 /*
46 * Graphics display driver for the X68K machines.
47 * This is the hardware-independent portion of the driver.
48 * Hardware access is through the machine dependent grf switch routines.
49 */
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/device.h>
54 #include <sys/proc.h>
55 #include <sys/ioctl.h>
56 #include <sys/file.h>
57 #include <sys/malloc.h>
58 #include <sys/vnode.h>
59 #include <sys/mman.h>
60
61 #include <x68k/dev/grfioctl.h>
62 #include <x68k/dev/grfvar.h>
63 #include <x68k/dev/itevar.h>
64
65 #include <machine/cpu.h>
66
67 #ifdef COMPAT_HPUX
68 #include <compat/hpux/hpux.h>
69 extern struct emul emul_hpux;
70 #endif
71
72 #include <vm/vm.h>
73 #include <vm/vm_kern.h>
74 #include <vm/vm_page.h>
75 #include <vm/vm_pager.h>
76
77 #include <miscfs/specfs/specdev.h>
78
79 #include "ite.h"
80 #if NITE == 0
81 #define iteon(u,f)
82 #define iteoff(u,f)
83 #endif
84
85 #ifdef DEBUG
86 int grfdebug = 0;
87 #define GDB_DEVNO 0x01
88 #define GDB_MMAP 0x02
89 #define GDB_IOMAP 0x04
90 #define GDB_LOCK 0x08
91 #endif
92
93 struct cfdriver grf_cd;
94
95 /*ARGSUSED*/
96 int
97 grfopen(dev, flags)
98 dev_t dev;
99 int flags;
100 {
101 int unit = GRFUNIT(dev);
102 register struct grf_softc *gp = grf_cd.cd_devs[unit];
103 int error = 0;
104
105 if (unit >= grf_cd.cd_ndevs || (gp->g_flags & GF_ALIVE) == 0)
106 return(ENXIO);
107 if ((gp->g_flags & (GF_OPEN|GF_EXCLUDE)) == (GF_OPEN|GF_EXCLUDE))
108 return(EBUSY);
109 #ifdef COMPAT_HPUX
110 /*
111 * XXX: cannot handle both HPUX and BSD processes at the same time
112 */
113 if (curproc->p_emul == &emul_hpux)
114 if (gp->g_flags & GF_BSDOPEN)
115 return(EBUSY);
116 else
117 gp->g_flags |= GF_HPUXOPEN;
118 else
119 if (gp->g_flags & GF_HPUXOPEN)
120 return(EBUSY);
121 else
122 gp->g_flags |= GF_BSDOPEN;
123 #endif
124 /*
125 * First open.
126 * XXX: always put in graphics mode.
127 */
128 error = 0;
129 if ((gp->g_flags & GF_OPEN) == 0) {
130 gp->g_flags |= GF_OPEN;
131 error = grfon(dev);
132 }
133 return(error);
134 }
135
136 /*ARGSUSED*/
137 int
138 grfclose(dev, flags)
139 dev_t dev;
140 int flags;
141 {
142 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
143
144 (void) grfoff(dev);
145 #ifdef COMPAT_HPUX
146 (void) grfunlock(gp);
147 #endif
148 gp->g_flags &= GF_ALIVE;
149 return(0);
150 }
151
152 /*ARGSUSED*/
153 int
154 grfioctl(dev, cmd, data, flag, p)
155 dev_t dev;
156 u_long cmd;
157 caddr_t data;
158 int flag;
159 struct proc *p;
160 {
161 int unit = GRFUNIT(dev);
162 register struct grf_softc *gp = grf_cd.cd_devs[unit];
163 int error;
164
165 #ifdef COMPAT_HPUX
166 if (p->p_emul == &emul_hpux)
167 return(hpuxgrfioctl(dev, cmd, data, flag, p));
168 #endif
169 error = 0;
170 switch (cmd) {
171
172 case GRFIOCGINFO:
173 bcopy((caddr_t)&gp->g_display, data, sizeof(struct grfinfo));
174 break;
175
176 case GRFIOCON:
177 error = grfon(dev);
178 break;
179
180 case GRFIOCOFF:
181 error = grfoff(dev);
182 break;
183
184 case GRFIOCMAP:
185 error = grfmap(dev, (caddr_t *)data, p);
186 break;
187
188 case GRFIOCUNMAP:
189 error = grfunmap(dev, *(caddr_t *)data, p);
190 break;
191
192 case GRFSETVMODE:
193 error = (*gp->g_sw->gd_mode)(gp, GM_GRFSETVMODE, data);
194 if (error == 0)
195 ite_reinit(unit);
196 break;
197
198 default:
199 error = EINVAL;
200 break;
201
202 }
203 return(error);
204 }
205
206 /*ARGSUSED*/
207 int
208 grfselect(dev, rw)
209 dev_t dev;
210 int rw;
211 {
212 if (rw == FREAD)
213 return(0);
214 return(1);
215 }
216
217 /*ARGSUSED*/
218 int
219 grfmmap(dev, off, prot)
220 dev_t dev;
221 int off, prot;
222 {
223 return (grfaddr(grf_cd.cd_devs[GRFUNIT(dev)], off));
224 }
225
226 int
227 grfon(dev)
228 dev_t dev;
229 {
230 int unit = GRFUNIT(dev);
231 struct grf_softc *gp = grf_cd.cd_devs[unit];
232
233 /*
234 * XXX: iteoff call relies on devices being in same order
235 * as ITEs and the fact that iteoff only uses the minor part
236 * of the dev arg.
237 */
238 iteoff(unit, 2);
239 return((*gp->g_sw->gd_mode)(gp,
240 (dev&GRFOVDEV) ? GM_GRFOVON : GM_GRFON,
241 (caddr_t)0));
242 }
243
244 int
245 grfoff(dev)
246 dev_t dev;
247 {
248 int unit = GRFUNIT(dev);
249 struct grf_softc *gp = grf_cd.cd_devs[unit];
250 int error;
251
252 (void) grfunmap(dev, (caddr_t)0, curproc);
253 error = (*gp->g_sw->gd_mode)(gp,
254 (dev&GRFOVDEV) ? GM_GRFOVOFF : GM_GRFOFF,
255 (caddr_t)0);
256 /* XXX: see comment for iteoff above */
257 iteon(unit, 2);
258 return(error);
259 }
260
261 int
262 grfaddr(gp, off)
263 struct grf_softc *gp;
264 register int off;
265 {
266 register struct grfinfo *gi = &gp->g_display;
267
268 /* control registers */
269 if (off >= 0 && off < gi->gd_regsize)
270 return(((u_int)gi->gd_regaddr + off) >> PGSHIFT);
271
272 /* frame buffer */
273 if (off >= gi->gd_regsize && off < gi->gd_regsize+gi->gd_fbsize) {
274 off -= gi->gd_regsize;
275 return(((u_int)gi->gd_fbaddr + off) >> PGSHIFT);
276 }
277 /* bogus */
278 return(-1);
279 }
280
281 /*
282 * HP-UX compatibility routines
283 */
284 #ifdef COMPAT_HPUX
285
286 /*ARGSUSED*/
287 hpuxgrfioctl(dev, cmd, data, flag, p)
288 dev_t dev;
289 u_long cmd;
290 caddr_t data;
291 int flag;
292 struct proc *p;
293 {
294 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
295 int error;
296
297 error = 0;
298 switch (cmd) {
299
300 case GCID:
301 *(int *)data = gp->g_display.gd_id;
302 break;
303
304 case GCON:
305 error = grfon(dev);
306 break;
307
308 case GCOFF:
309 error = grfoff(dev);
310 break;
311
312 case GCLOCK:
313 error = grflock(gp, 1);
314 break;
315
316 case GCUNLOCK:
317 error = grfunlock(gp);
318 break;
319
320 case GCAON:
321 case GCAOFF:
322 break;
323
324 /* GCSTATIC is implied by our implementation */
325 case GCSTATIC_CMAP:
326 case GCVARIABLE_CMAP:
327 break;
328
329 /* map in control regs and frame buffer */
330 case GCMAP:
331 error = grfmap(dev, (caddr_t *)data, p);
332 break;
333
334 case GCUNMAP:
335 error = grfunmap(dev, *(caddr_t *)data, p);
336 /* XXX: HP-UX uses GCUNMAP to get rid of GCSLOT memory */
337 if (error)
338 error = grflckunmmap(dev, *(caddr_t *)data);
339 break;
340
341 case GCSLOT:
342 {
343 struct grf_slot *sp = (struct grf_slot *)data;
344
345 sp->slot = grffindpid(gp);
346 if (sp->slot) {
347 error = grflckmmap(dev, (caddr_t *)&sp->addr);
348 if (error && gp->g_pid) {
349 free((caddr_t)gp->g_pid, M_DEVBUF);
350 gp->g_pid = NULL;
351 }
352 } else
353 error = EINVAL; /* XXX */
354 break;
355 }
356
357 case GCDESCRIBE:
358 error = (*gp->g_sw->gd_mode)(gp, GM_DESCRIBE, data);
359 break;
360
361 /*
362 * XXX: only used right now to map in rbox control registers
363 * Will be replaced in the future with a real IOMAP interface.
364 */
365 case IOMAPMAP:
366 error = iommap(dev, (caddr_t *)data);
367 #if 0
368 /*
369 * It may not be worth kludging this (using p_devtmp) to
370 * make this work. It was an undocumented side-effect
371 * in HP-UX that the mapped address was the return value
372 * of the ioctl. The only thing I remember that counted
373 * on this behavior was the rbox X10 server.
374 */
375 if (!error)
376 u.u_r.r_val1 = *(int *)data; /* XXX: this sux */
377 #endif
378 break;
379
380 case IOMAPUNMAP:
381 error = iounmmap(dev, *(caddr_t *)data);
382 break;
383
384 default:
385 error = EINVAL;
386 break;
387 }
388 return(error);
389 }
390
391 grflock(gp, block)
392 register struct grf_softc *gp;
393 int block;
394 {
395 struct proc *p = curproc; /* XXX */
396 int error;
397 extern char devioc[];
398
399 #ifdef DEBUG
400 if (grfdebug & GDB_LOCK)
401 printf("grflock(%d): dev %x flags %x lockpid %x\n",
402 p->p_pid, gp-grf_softc, gp->g_flags,
403 gp->g_lockp ? gp->g_lockp->p_pid : -1);
404 #endif
405 if (gp->g_pid) {
406 #ifdef DEBUG
407 if (grfdebug & GDB_LOCK)
408 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n",
409 gp->g_lock->gl_lockslot, gp->g_lockpslot,
410 gp->g_lock->gl_locks[gp->g_lockpslot]);
411 #endif
412 gp->g_lock->gl_lockslot = 0;
413 if (gp->g_lock->gl_locks[gp->g_lockpslot] == 0) {
414 gp->g_lockp = NULL;
415 gp->g_lockpslot = 0;
416 }
417 }
418 if (gp->g_lockp) {
419 if (gp->g_lockp == p)
420 return(EBUSY);
421 if (!block)
422 return(OEAGAIN);
423 do {
424 gp->g_flags |= GF_WANTED;
425 if (error = tsleep((caddr_t)&gp->g_flags,
426 (PZERO+1) | PCATCH, devioc, 0))
427 return (error);
428 } while (gp->g_lockp);
429 }
430 gp->g_lockp = p;
431 if (gp->g_pid) {
432 int slot = grffindpid(gp);
433
434 #ifdef DEBUG
435 if (grfdebug & GDB_LOCK)
436 printf(" slot %d\n", slot);
437 #endif
438 gp->g_lockpslot = gp->g_lock->gl_lockslot = slot;
439 gp->g_lock->gl_locks[slot] = 1;
440 }
441 return(0);
442 }
443
444 grfunlock(gp)
445 register struct grf_softc *gp;
446 {
447 #ifdef DEBUG
448 if (grfdebug & GDB_LOCK)
449 printf("grfunlock(%d): dev %x flags %x lockpid %d\n",
450 curproc->p_pid, gp-grf_softc, gp->g_flags,
451 gp->g_lockp ? gp->g_lockp->p_pid : -1);
452 #endif
453 if (gp->g_lockp != curproc)
454 return(EBUSY);
455 if (gp->g_pid) {
456 #ifdef DEBUG
457 if (grfdebug & GDB_LOCK)
458 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n",
459 gp->g_lock->gl_lockslot, gp->g_lockpslot,
460 gp->g_lock->gl_locks[gp->g_lockpslot]);
461 #endif
462 gp->g_lock->gl_locks[gp->g_lockpslot] = 0;
463 gp->g_lockpslot = gp->g_lock->gl_lockslot = 0;
464 }
465 if (gp->g_flags & GF_WANTED) {
466 wakeup((caddr_t)&gp->g_flags);
467 gp->g_flags &= ~GF_WANTED;
468 }
469 gp->g_lockp = NULL;
470 return(0);
471 }
472
473 /*
474 * Convert a BSD style minor devno to HPUX style.
475 * We cannot just create HPUX style nodes as they require 24 bits
476 * of minor device number and we only have 8.
477 * XXX: This may give the wrong result for remote stats of other
478 * machines where device 10 exists.
479 */
480 grfdevno(dev)
481 dev_t dev;
482 {
483 int unit = GRFUNIT(dev);
484 struct grf_softc *gp = grf_cd.cd_devs[unit];
485 int newdev;
486
487 if (unit >= grf_cd.cd_ndevs || (gp->g_flags&GF_ALIVE) == 0)
488 return(bsdtohpuxdev(dev));
489 /* magic major number */
490 newdev = 12 << 24;
491 /* now construct minor number */
492 if (gp->g_display.gd_regaddr != (caddr_t)GRFIADDR) {
493 int sc = patosc(gp->g_display.gd_regaddr);
494 newdev |= (sc << 16) | 0x200;
495 }
496 if (dev & GRFIMDEV)
497 newdev |= 0x02;
498 else if (dev & GRFOVDEV)
499 newdev |= 0x01;
500 #ifdef DEBUG
501 if (grfdebug & GDB_DEVNO)
502 printf("grfdevno: dev %x newdev %x\n", dev, newdev);
503 #endif
504 return(newdev);
505 }
506
507 #endif /* COMPAT_HPUX */
508
509 int
510 grfmap(dev, addrp, p)
511 dev_t dev;
512 caddr_t *addrp;
513 struct proc *p;
514 {
515 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
516 int len, error;
517 struct vnode vn;
518 struct specinfo si;
519 int flags;
520
521 #ifdef DEBUG
522 if (grfdebug & GDB_MMAP)
523 printf("grfmap(%d): addr %p\n", p->p_pid, *addrp);
524 #endif
525 len = gp->g_display.gd_regsize + gp->g_display.gd_fbsize;
526 flags = MAP_SHARED;
527 if (*addrp)
528 flags |= MAP_FIXED;
529 else
530 *addrp = (caddr_t)0x1000000; /* XXX */
531 vn.v_type = VCHR; /* XXX */
532 vn.v_specinfo = &si; /* XXX */
533 vn.v_rdev = dev; /* XXX */
534 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp,
535 (vm_size_t)len, VM_PROT_ALL, VM_PROT_ALL,
536 flags, (caddr_t)&vn, 0);
537 if (error == 0)
538 (void) (*gp->g_sw->gd_mode)(gp, GM_MAP, *addrp);
539 return(error);
540 }
541
542 int
543 grfunmap(dev, addr, p)
544 dev_t dev;
545 caddr_t addr;
546 struct proc *p;
547 {
548 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
549 vm_size_t size;
550 int rv;
551
552 #ifdef DEBUG
553 if (grfdebug & GDB_MMAP)
554 printf("grfunmap(%d): dev %x addr %p\n", p->p_pid, dev, addr);
555 #endif
556 if (addr == 0)
557 return(EINVAL); /* XXX: how do we deal with this? */
558 (void) (*gp->g_sw->gd_mode)(gp, GM_UNMAP, 0);
559 size = round_page(gp->g_display.gd_regsize + gp->g_display.gd_fbsize);
560 rv = vm_deallocate(&p->p_vmspace->vm_map, (vm_offset_t)addr, size);
561 return(rv == KERN_SUCCESS ? 0 : EINVAL);
562 }
563
564 #ifdef COMPAT_HPUX
565 iommap(dev, addrp)
566 dev_t dev;
567 caddr_t *addrp;
568 {
569
570 #ifdef DEBUG
571 if (grfdebug & (GDB_MMAP|GDB_IOMAP))
572 printf("iommap(%d): addr %x\n", curproc->p_pid, *addrp);
573 #endif
574 return(EINVAL);
575 }
576
577 iounmmap(dev, addr)
578 dev_t dev;
579 caddr_t addr;
580 {
581 int unit = minor(dev);
582
583 #ifdef DEBUG
584 if (grfdebug & (GDB_MMAP|GDB_IOMAP))
585 printf("iounmmap(%d): id %d addr %x\n",
586 curproc->p_pid, unit, addr);
587 #endif
588 return(0);
589 }
590
591 /*
592 * Processes involved in framebuffer mapping via GCSLOT are recorded in
593 * an array of pids. The first element is used to record the last slot used
594 * (for faster lookups). The remaining elements record up to GRFMAXLCK-1
595 * process ids. Returns a slot number between 1 and GRFMAXLCK or 0 if no
596 * slot is available.
597 */
598 grffindpid(gp)
599 struct grf_softc *gp;
600 {
601 register short pid, *sp;
602 register int i, limit;
603 int ni;
604
605 if (gp->g_pid == NULL) {
606 gp->g_pid = (short *)
607 malloc(GRFMAXLCK * sizeof(short), M_DEVBUF, M_WAITOK);
608 bzero((caddr_t)gp->g_pid, GRFMAXLCK * sizeof(short));
609 }
610 pid = curproc->p_pid;
611 ni = limit = gp->g_pid[0];
612 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) {
613 if (*sp == pid)
614 goto done;
615 if (*sp == 0)
616 ni = i;
617 }
618 i = ni;
619 if (i < limit) {
620 gp->g_pid[i] = pid;
621 goto done;
622 }
623 if (++i == GRFMAXLCK)
624 return(0);
625 gp->g_pid[0] = i;
626 gp->g_pid[i] = pid;
627 done:
628 #ifdef DEBUG
629 if (grfdebug & GDB_LOCK)
630 printf("grffindpid(%d): slot %d of %d\n",
631 pid, i, gp->g_pid[0]);
632 #endif
633 return(i);
634 }
635
636 grfrmpid(gp)
637 struct grf_softc *gp;
638 {
639 register short pid, *sp;
640 register int limit, i;
641 int mi;
642
643 if (gp->g_pid == NULL || (limit = gp->g_pid[0]) == 0)
644 return;
645 pid = curproc->p_pid;
646 limit = gp->g_pid[0];
647 mi = 0;
648 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) {
649 if (*sp == pid)
650 *sp = 0;
651 else if (*sp)
652 mi = i;
653 }
654 i = mi;
655 if (i < limit)
656 gp->g_pid[0] = i;
657 #ifdef DEBUG
658 if (grfdebug & GDB_LOCK)
659 printf("grfrmpid(%d): slot %d of %d\n",
660 pid, sp-gp->g_pid, gp->g_pid[0]);
661 #endif
662 }
663
664 grflckmmap(dev, addrp)
665 dev_t dev;
666 caddr_t *addrp;
667 {
668 #ifdef DEBUG
669 struct proc *p = curproc; /* XXX */
670
671 if (grfdebug & (GDB_MMAP|GDB_LOCK))
672 printf("grflckmmap(%d): addr %x\n",
673 p->p_pid, *addrp);
674 #endif
675 return(EINVAL);
676 }
677
678 grflckunmmap(dev, addr)
679 dev_t dev;
680 caddr_t addr;
681 {
682 #ifdef DEBUG
683 int unit = minor(dev);
684
685 if (grfdebug & (GDB_MMAP|GDB_LOCK))
686 printf("grflckunmmap(%d): id %d addr %x\n",
687 curproc->p_pid, unit, addr);
688 #endif
689 return(EINVAL);
690 }
691 #endif /* COMPAT_HPUX */
692