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