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