[net.unix-wizards] Xenix graphics device driver help

robin@medstar.UUCP (Robin Cutshaw) (08/21/85)

[THE FIRST LINE]


I have had so many requests for info on writing device drivers for the CGA
and EGA that I thought I would post a few excerpts from my first driver for
these.  The finished drivers have changed significantly and have been
heavily optimized but I think this will suffice (not sure that I will post
or market the finished drivers).

This just takes a structure containing an offset into screen memory and the
char to write and does it.  This is veeeerrry slow.

extern short hgastart; /* use cgastart for cga, hgastart for hga and ega */
    case CRT_WR:
                copyin(arg,&crt_s,sizeof(struct crt_s)); /* we could use arg
                                                            and cmd but... */
                
                crt_addr = ( (hgastart * 2) +
                    ( ( ((unsigned long )crt_s.crt_offset>>16)&0xff ) * 0xa0 ) +
                    ((unsigned long )crt_s.crt_offset&0xffff) );
                crt_addr &= 0x3fff; /* the [ceh]ga driver uses only 16K */
                *( (faddr_t )( 0xc80000 + crt_addr ) ) = crt_s.crt_uchar;
                return;


This routine actually returns a pointer to the [ceh]ga memory area for use
by a user process.  It must find an open LDT slot above FIRSTU_SEL in mmu.h
and fill it with the appropriate LDT entry.  No heavy checking is done so
one might get a panic if not carefull.

    case CRT_MAP:
                copyin(arg,&mem_s,sizeof(struct mem_s));
                mem_s.mem_addr = (faddr_t )0L;
                for (i=FIRSTU_SEL & 0xf8; i < dscrlimit(ULDT_SEL)-8 ; i += 8) {
                    mem_s.mem_addr = (faddr_t)((((long )ULDT_SEL)<<16) + (i+5));
                    if (*mem_s.mem_addr == 0) {  /* weak check */
                        mem_s.mem_addr = (faddr_t)((((long )ULDT_SEL)<<16) + i);
                                /* seems to be some funnyness with sotofar() */
                        *mem_s.mem_addr++ = 0xff; /* limit low */
                        *mem_s.mem_addr++ = 0xff; /* limit hi */
                        *mem_s.mem_addr++ = 0x00; /* base low */
                        *mem_s.mem_addr++ = 0x80; /* base hi */
                        *mem_s.mem_addr++ = 0x0b; /* base seg */
                        *mem_s.mem_addr++ = 0xf2; /* access byte */
                        *mem_s.mem_addr++ = 0x00;
                        *mem_s.mem_addr++ = 0x00;
                        mem_s.mem_addr = sotofar((i | 7),0);
                        break;
                    }
                    mem_s.mem_addr = sotofar(0,0);
                }
                copyout(&mem_s,arg,sizeof(struct mem_s));
                return;

Note that one could do this very simply by changing the access byte in the
GDT for user level access but this would give everyone on the system access
and this just isn't the "UNIX way". Also, one can add ioctl's for io-addr
space to program the [ceh]ga control registers (which is a natural extension).

Most of the secrets are found in sys/param.h and sys/mmu.h.  From these and
the Intel manuals on the 286 it is pretty simple to write just about any
kind of driver (that is if you are used to writing drivers for other unix
systems).  I have written one for the PC Network, the EGA, and I am now
working on the mountain tape backup system (a real pain).

Also note that the adapter memory is not accessable from /dev/anymem since
this is not considered available memory for use, it is mapped to selector
0xC8 for GDT use in protected level 0 only.


-robin
-- 
----
Robin Cutshaw
uucp:   ...!{akgua,gatech}!medstar!robin