grf.c revision 1.13 1 /* $NetBSD: grf.c,v 1.13 1999/03/24 05:51:17 mrg 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 "opt_compat_hpux.h"
52
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/device.h>
56 #include <sys/proc.h>
57 #include <sys/ioctl.h>
58 #include <sys/file.h>
59 #include <sys/malloc.h>
60 #include <sys/vnode.h>
61 #include <sys/mman.h>
62 #include <sys/poll.h>
63 #include <sys/conf.h>
64
65 #include <machine/grfioctl.h>
66
67 #include <x68k/dev/grfvar.h>
68 #include <x68k/dev/itevar.h>
69
70 #include <machine/cpu.h>
71
72 #ifdef COMPAT_HPUX
73 #include <compat/hpux/hpux.h>
74 extern struct emul emul_hpux;
75 #endif
76
77 #include <vm/vm.h>
78 #include <vm/vm_kern.h>
79 #include <vm/vm_page.h>
80 #include <vm/vm_pager.h>
81
82 #include <uvm/uvm_extern.h>
83
84 #include <miscfs/specfs/specdev.h>
85
86 #include "ite.h"
87 #if NITE == 0
88 #define iteon(u,f)
89 #define iteoff(u,f)
90 #define ite_reinit(u)
91 #endif
92
93 #ifdef DEBUG
94 int grfdebug = 0;
95 #define GDB_DEVNO 0x01
96 #define GDB_MMAP 0x02
97 #define GDB_IOMAP 0x04
98 #define GDB_LOCK 0x08
99 #endif
100
101 cdev_decl(grf);
102 int grfon __P((dev_t));
103 int grfoff __P((dev_t));
104 int grfaddr __P((struct grf_softc *, int));
105 int grfmap __P((dev_t, caddr_t *, struct proc *));
106 int grfunmap __P((dev_t, caddr_t, struct proc *));
107
108 extern struct cfdriver grf_cd;
109
110 /*ARGSUSED*/
111 int
112 grfopen(dev, flags, mode, p)
113 dev_t dev;
114 int flags, mode;
115 struct proc *p;
116 {
117 int unit = GRFUNIT(dev);
118 register struct grf_softc *gp;
119 int error = 0;
120
121 if (unit >= grf_cd.cd_ndevs ||
122 (gp = grf_cd.cd_devs[unit]) == NULL ||
123 (gp->g_flags & GF_ALIVE) == 0)
124 return (ENXIO);
125
126 if ((gp->g_flags & (GF_OPEN|GF_EXCLUDE)) == (GF_OPEN|GF_EXCLUDE))
127 return(EBUSY);
128 #ifdef COMPAT_HPUX
129 /*
130 * XXX: cannot handle both HPUX and BSD processes at the same time
131 */
132 if (p->p_emul == &emul_hpux)
133 if (gp->g_flags & GF_BSDOPEN)
134 return(EBUSY);
135 else
136 gp->g_flags |= GF_HPUXOPEN;
137 else
138 if (gp->g_flags & GF_HPUXOPEN)
139 return(EBUSY);
140 else
141 gp->g_flags |= GF_BSDOPEN;
142 #endif
143 /*
144 * First open.
145 * XXX: always put in graphics mode.
146 */
147 error = 0;
148 if ((gp->g_flags & GF_OPEN) == 0) {
149 gp->g_flags |= GF_OPEN;
150 error = grfon(dev);
151 }
152 return(error);
153 }
154
155 /*ARGSUSED*/
156 int
157 grfclose(dev, flags, mode, p)
158 dev_t dev;
159 int flags, mode;
160 struct proc *p;
161 {
162 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
163
164 if ((gp->g_flags & GF_ALIVE) == 0)
165 return (ENXIO);
166
167 (void) grfoff(dev);
168 #ifdef COMPAT_HPUX
169 (void) grfunlock(gp);
170 #endif
171 gp->g_flags &= GF_ALIVE;
172 return(0);
173 }
174
175 /*ARGSUSED*/
176 int
177 grfioctl(dev, cmd, data, flag, p)
178 dev_t dev;
179 u_long cmd;
180 caddr_t data;
181 int flag;
182 struct proc *p;
183 {
184 int unit = GRFUNIT(dev);
185 register struct grf_softc *gp = grf_cd.cd_devs[unit];
186 int error;
187
188 if ((gp->g_flags & GF_ALIVE) == 0)
189 return (ENXIO);
190
191 #ifdef COMPAT_HPUX
192 if (p->p_emul == &emul_hpux)
193 return(hpuxgrfioctl(dev, cmd, data, flag, p));
194 #endif
195 error = 0;
196 switch (cmd) {
197
198 case GRFIOCGINFO:
199 bcopy((caddr_t)&gp->g_display, data, sizeof(struct grfinfo));
200 break;
201
202 case GRFIOCON:
203 error = grfon(dev);
204 break;
205
206 case GRFIOCOFF:
207 error = grfoff(dev);
208 break;
209
210 case GRFIOCMAP:
211 error = grfmap(dev, (caddr_t *)data, p);
212 break;
213
214 case GRFIOCUNMAP:
215 error = grfunmap(dev, *(caddr_t *)data, p);
216 break;
217
218 case GRFSETVMODE:
219 error = (*gp->g_sw->gd_mode)(gp, GM_GRFSETVMODE, data);
220 if (error == 0)
221 ite_reinit(unit);
222 break;
223
224 default:
225 error = EINVAL;
226 break;
227
228 }
229 return(error);
230 }
231
232 /*ARGSUSED*/
233 int
234 grfpoll(dev, events, p)
235 dev_t dev;
236 int events;
237 struct proc *p;
238 {
239
240 return (events & (POLLOUT | POLLWRNORM));
241 }
242
243 /*ARGSUSED*/
244 int
245 grfmmap(dev, off, prot)
246 dev_t dev;
247 int off, prot;
248 {
249
250 return (grfaddr(grf_cd.cd_devs[GRFUNIT(dev)], off));
251 }
252
253 int
254 grfon(dev)
255 dev_t dev;
256 {
257 int unit = GRFUNIT(dev);
258 struct grf_softc *gp = grf_cd.cd_devs[unit];
259
260 /*
261 * XXX: iteoff call relies on devices being in same order
262 * as ITEs and the fact that iteoff only uses the minor part
263 * of the dev arg.
264 */
265 iteoff(unit, 2);
266 return((*gp->g_sw->gd_mode)(gp,
267 (dev&GRFOVDEV) ? GM_GRFOVON : GM_GRFON,
268 (caddr_t)0));
269 }
270
271 int
272 grfoff(dev)
273 dev_t dev;
274 {
275 int unit = GRFUNIT(dev);
276 struct grf_softc *gp = grf_cd.cd_devs[unit];
277 int error;
278
279 (void) grfunmap(dev, (caddr_t)0, curproc);
280 error = (*gp->g_sw->gd_mode)(gp,
281 (dev&GRFOVDEV) ? GM_GRFOVOFF : GM_GRFOFF,
282 (caddr_t)0);
283 /* XXX: see comment for iteoff above */
284 iteon(unit, 2);
285 return(error);
286 }
287
288 int
289 grfaddr(gp, off)
290 struct grf_softc *gp;
291 register int off;
292 {
293 register struct grfinfo *gi = &gp->g_display;
294
295 /* control registers */
296 if (off >= 0 && off < gi->gd_regsize)
297 return(((u_int)gi->gd_regaddr + off) >> PGSHIFT);
298
299 /* frame buffer */
300 if (off >= gi->gd_regsize && off < gi->gd_regsize+gi->gd_fbsize) {
301 off -= gi->gd_regsize;
302 return(((u_int)gi->gd_fbaddr + off) >> PGSHIFT);
303 }
304 /* bogus */
305 return(-1);
306 }
307
308 /*
309 * HP-UX compatibility routines
310 */
311 #ifdef COMPAT_HPUX
312
313 /*ARGSUSED*/
314 int
315 hpuxgrfioctl(dev, cmd, data, flag, p)
316 dev_t dev;
317 int cmd;
318 caddr_t data;
319 int flag;
320 struct proc *p;
321 {
322 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
323 int error;
324
325 error = 0;
326 switch (cmd) {
327
328 case GCID:
329 *(int *)data = gp->g_display.gd_id;
330 break;
331
332 case GCON:
333 error = grfon(dev);
334 break;
335
336 case GCOFF:
337 error = grfoff(dev);
338 break;
339
340 case GCLOCK:
341 error = grflock(gp, 1);
342 break;
343
344 case GCUNLOCK:
345 error = grfunlock(gp);
346 break;
347
348 case GCAON:
349 case GCAOFF:
350 break;
351
352 /* GCSTATIC is implied by our implementation */
353 case GCSTATIC_CMAP:
354 case GCVARIABLE_CMAP:
355 break;
356
357 /* map in control regs and frame buffer */
358 case GCMAP:
359 error = grfmap(dev, (caddr_t *)data, p);
360 break;
361
362 case GCUNMAP:
363 error = grfunmap(dev, *(caddr_t *)data, p);
364 /* XXX: HP-UX uses GCUNMAP to get rid of GCSLOT memory */
365 if (error)
366 error = grflckunmmap(dev, *(caddr_t *)data);
367 break;
368
369 case GCSLOT:
370 {
371 struct grf_slot *sp = (struct grf_slot *)data;
372
373 sp->slot = grffindpid(gp);
374 if (sp->slot) {
375 error = grflckmmap(dev, (caddr_t *)&sp->addr);
376 if (error && gp->g_pid) {
377 free((caddr_t)gp->g_pid, M_DEVBUF);
378 gp->g_pid = NULL;
379 }
380 } else
381 error = EINVAL; /* XXX */
382 break;
383 }
384
385 case GCDESCRIBE:
386 error = (*gp->g_sw->gd_mode)(gp, GM_DESCRIBE, data);
387 break;
388
389 /*
390 * XXX: only used right now to map in rbox control registers
391 * Will be replaced in the future with a real IOMAP interface.
392 */
393 case IOMAPMAP:
394 error = iommap(dev, (caddr_t *)data);
395 #if 0
396 /*
397 * It may not be worth kludging this (using p_devtmp) to
398 * make this work. It was an undocumented side-effect
399 * in HP-UX that the mapped address was the return value
400 * of the ioctl. The only thing I remember that counted
401 * on this behavior was the rbox X10 server.
402 */
403 if (!error)
404 u.u_r.r_val1 = *(int *)data; /* XXX: this sux */
405 #endif
406 break;
407
408 case IOMAPUNMAP:
409 error = iounmmap(dev, *(caddr_t *)data);
410 break;
411
412 default:
413 error = EINVAL;
414 break;
415 }
416 return(error);
417 }
418
419 int
420 grflock(gp, block)
421 register struct grf_softc *gp;
422 int block;
423 {
424 struct proc *p = curproc; /* XXX */
425 int error;
426 extern char devioc[];
427
428 #ifdef DEBUG
429 if (grfdebug & GDB_LOCK)
430 printf("grflock(%d): flags %x lockpid %x\n",
431 p->p_pid, gp->g_flags,
432 gp->g_lockp ? gp->g_lockp->p_pid : -1);
433 #endif
434 if (gp->g_pid) {
435 #ifdef DEBUG
436 if (grfdebug & GDB_LOCK)
437 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n",
438 gp->g_lock->gl_lockslot, gp->g_lockpslot,
439 gp->g_lock->gl_locks[gp->g_lockpslot]);
440 #endif
441 gp->g_lock->gl_lockslot = 0;
442 if (gp->g_lock->gl_locks[gp->g_lockpslot] == 0) {
443 gp->g_lockp = NULL;
444 gp->g_lockpslot = 0;
445 }
446 }
447 if (gp->g_lockp) {
448 if (gp->g_lockp == p)
449 return(EBUSY);
450 if (!block)
451 return(OEAGAIN);
452 do {
453 gp->g_flags |= GF_WANTED;
454 if ((error = tsleep((caddr_t)&gp->g_flags,
455 (PZERO+1) | PCATCH, devioc, 0)))
456 return (error);
457 } while (gp->g_lockp);
458 }
459 gp->g_lockp = p;
460 if (gp->g_pid) {
461 int slot = grffindpid(gp);
462
463 #ifdef DEBUG
464 if (grfdebug & GDB_LOCK)
465 printf(" slot %d\n", slot);
466 #endif
467 gp->g_lockpslot = gp->g_lock->gl_lockslot = slot;
468 gp->g_lock->gl_locks[slot] = 1;
469 }
470 return(0);
471 }
472
473 int
474 grfunlock(gp)
475 register struct grf_softc *gp;
476 {
477 #ifdef DEBUG
478 if (grfdebug & GDB_LOCK)
479 printf("grfunlock(%d): flags %x lockpid %d\n",
480 curproc->p_pid, gp->g_flags,
481 gp->g_lockp ? gp->g_lockp->p_pid : -1);
482 #endif
483 if (gp->g_lockp != curproc)
484 return(EBUSY);
485 if (gp->g_pid) {
486 #ifdef DEBUG
487 if (grfdebug & GDB_LOCK)
488 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n",
489 gp->g_lock->gl_lockslot, gp->g_lockpslot,
490 gp->g_lock->gl_locks[gp->g_lockpslot]);
491 #endif
492 gp->g_lock->gl_locks[gp->g_lockpslot] = 0;
493 gp->g_lockpslot = gp->g_lock->gl_lockslot = 0;
494 }
495 if (gp->g_flags & GF_WANTED) {
496 wakeup((caddr_t)&gp->g_flags);
497 gp->g_flags &= ~GF_WANTED;
498 }
499 gp->g_lockp = NULL;
500 return(0);
501 }
502
503 /*
504 * Convert a BSD style minor devno to HPUX style.
505 * We cannot just create HPUX style nodes as they require 24 bits
506 * of minor device number and we only have 8.
507 * XXX: This may give the wrong result for remote stats of other
508 * machines where device 10 exists.
509 */
510 int
511 grfdevno(dev)
512 dev_t dev;
513 {
514 int unit = GRFUNIT(dev);
515 struct grf_softc *gp;
516 int newdev;
517
518 if (unit >= grf_cd.cd_ndevs ||
519 (gp = grf_cd.cd_devs[unit]) == NULL ||
520 (gp->g_flags&GF_ALIVE) == 0)
521 return(bsdtohpuxdev(dev));
522 /* magic major number */
523 newdev = 12 << 24;
524 /* now construct minor number */
525 if (gp->g_display.gd_regaddr != (caddr_t)GRFIADDR) {
526 int sc = patosc(gp->g_display.gd_regaddr);
527 newdev |= (sc << 16) | 0x200;
528 }
529 if (dev & GRFIMDEV)
530 newdev |= 0x02;
531 else if (dev & GRFOVDEV)
532 newdev |= 0x01;
533 #ifdef DEBUG
534 if (grfdebug & GDB_DEVNO)
535 printf("grfdevno: dev %x newdev %x\n", dev, newdev);
536 #endif
537 return(newdev);
538 }
539
540 #endif /* COMPAT_HPUX */
541
542 int
543 grfmap(dev, addrp, p)
544 dev_t dev;
545 caddr_t *addrp;
546 struct proc *p;
547 {
548 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
549 int len, error;
550 struct vnode vn;
551 struct specinfo si;
552 int flags;
553
554 #ifdef DEBUG
555 if (grfdebug & GDB_MMAP)
556 printf("grfmap(%d): addr %p\n", p->p_pid, *addrp);
557 #endif
558 len = gp->g_display.gd_regsize + gp->g_display.gd_fbsize;
559 flags = MAP_SHARED;
560 if (*addrp)
561 flags |= MAP_FIXED;
562 else
563 *addrp = (caddr_t)0x1000000; /* XXX */
564 vn.v_type = VCHR; /* XXX */
565 vn.v_specinfo = &si; /* XXX */
566 vn.v_rdev = dev; /* XXX */
567 error = uvm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp,
568 (vsize_t)len, VM_PROT_ALL, VM_PROT_ALL,
569 flags, (caddr_t)&vn, 0);
570 if (error == 0)
571 (void) (*gp->g_sw->gd_mode)(gp, GM_MAP, *addrp);
572 return(error);
573 }
574
575 int
576 grfunmap(dev, addr, p)
577 dev_t dev;
578 caddr_t addr;
579 struct proc *p;
580 {
581 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)];
582 vsize_t size;
583 int rv;
584
585 #ifdef DEBUG
586 if (grfdebug & GDB_MMAP)
587 printf("grfunmap(%d): dev %x addr %p\n", p->p_pid, dev, addr);
588 #endif
589 if (addr == 0)
590 return(EINVAL); /* XXX: how do we deal with this? */
591 (void) (*gp->g_sw->gd_mode)(gp, GM_UNMAP, 0);
592 size = round_page(gp->g_display.gd_regsize + gp->g_display.gd_fbsize);
593 rv = uvm_unmap(&p->p_vmspace->vm_map, (vaddr_t)addr,
594 (vaddr_t)addr + size);
595 return(rv == KERN_SUCCESS ? 0 : EINVAL);
596 }
597
598 #ifdef COMPAT_HPUX
599 int
600 iommap(dev, addrp)
601 dev_t dev;
602 caddr_t *addrp;
603 {
604
605 #ifdef DEBUG
606 if (grfdebug & (GDB_MMAP|GDB_IOMAP))
607 printf("iommap(%d): addr %p\n", curproc->p_pid, *addrp);
608 #endif
609 return(EINVAL);
610 }
611
612 int
613 iounmmap(dev, addr)
614 dev_t dev;
615 caddr_t addr;
616 {
617 #ifdef DEBUG
618 int unit = minor(dev);
619
620 if (grfdebug & (GDB_MMAP|GDB_IOMAP))
621 printf("iounmmap(%d): id %d addr %p\n",
622 curproc->p_pid, unit, addr);
623 #endif
624 return(0);
625 }
626
627 /*
628 * Processes involved in framebuffer mapping via GCSLOT are recorded in
629 * an array of pids. The first element is used to record the last slot used
630 * (for faster lookups). The remaining elements record up to GRFMAXLCK-1
631 * process ids. Returns a slot number between 1 and GRFMAXLCK or 0 if no
632 * slot is available.
633 */
634 int
635 grffindpid(gp)
636 struct grf_softc *gp;
637 {
638 register short pid, *sp;
639 register int i, limit;
640 int ni;
641
642 if (gp->g_pid == NULL) {
643 gp->g_pid = (short *)
644 malloc(GRFMAXLCK * sizeof(short), M_DEVBUF, M_WAITOK);
645 bzero((caddr_t)gp->g_pid, GRFMAXLCK * sizeof(short));
646 }
647 pid = curproc->p_pid;
648 ni = limit = gp->g_pid[0];
649 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) {
650 if (*sp == pid)
651 goto done;
652 if (*sp == 0)
653 ni = i;
654 }
655 i = ni;
656 if (i < limit) {
657 gp->g_pid[i] = pid;
658 goto done;
659 }
660 if (++i == GRFMAXLCK)
661 return(0);
662 gp->g_pid[0] = i;
663 gp->g_pid[i] = pid;
664 done:
665 #ifdef DEBUG
666 if (grfdebug & GDB_LOCK)
667 printf("grffindpid(%d): slot %d of %d\n",
668 pid, i, gp->g_pid[0]);
669 #endif
670 return(i);
671 }
672
673 void
674 grfrmpid(gp)
675 struct grf_softc *gp;
676 {
677 register short pid, *sp;
678 register int limit, i;
679 int mi;
680
681 if (gp->g_pid == NULL || (limit = gp->g_pid[0]) == 0)
682 return;
683 pid = curproc->p_pid;
684 limit = gp->g_pid[0];
685 mi = 0;
686 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) {
687 if (*sp == pid)
688 *sp = 0;
689 else if (*sp)
690 mi = i;
691 }
692 i = mi;
693 if (i < limit)
694 gp->g_pid[0] = i;
695 #ifdef DEBUG
696 if (grfdebug & GDB_LOCK)
697 printf("grfrmpid(%d): slot %d of %d\n",
698 pid, sp-gp->g_pid, gp->g_pid[0]);
699 #endif
700 }
701
702 int
703 grflckmmap(dev, addrp)
704 dev_t dev;
705 caddr_t *addrp;
706 {
707 #ifdef DEBUG
708 struct proc *p = curproc; /* XXX */
709
710 if (grfdebug & (GDB_MMAP|GDB_LOCK))
711 printf("grflckmmap(%d): addr %p\n",
712 p->p_pid, *addrp);
713 #endif
714 return(EINVAL);
715 }
716
717 int
718 grflckunmmap(dev, addr)
719 dev_t dev;
720 caddr_t addr;
721 {
722 #ifdef DEBUG
723 int unit = minor(dev);
724
725 if (grfdebug & (GDB_MMAP|GDB_LOCK))
726 printf("grflckunmmap(%d): id %d addr %p\n",
727 curproc->p_pid, unit, addr);
728 #endif
729 return(EINVAL);
730 }
731 #endif /* COMPAT_HPUX */
732