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