diff options
Diffstat (limited to 'tools/ddcprobe')
-rw-r--r-- | tools/ddcprobe/.cvsignore | 1 | ||||
-rw-r--r-- | tools/ddcprobe/Makefile | 18 | ||||
-rw-r--r-- | tools/ddcprobe/ddcxinfos.c | 66 | ||||
-rw-r--r-- | tools/ddcprobe/lrmi.c | 911 | ||||
-rw-r--r-- | tools/ddcprobe/lrmi.h | 85 | ||||
-rw-r--r-- | tools/ddcprobe/vbe.c | 709 | ||||
-rw-r--r-- | tools/ddcprobe/vbe.h | 312 | ||||
-rw-r--r-- | tools/ddcprobe/vesamode.c | 132 | ||||
-rw-r--r-- | tools/ddcprobe/vesamode.h | 31 |
9 files changed, 2265 insertions, 0 deletions
diff --git a/tools/ddcprobe/.cvsignore b/tools/ddcprobe/.cvsignore new file mode 100644 index 000000000..c3c3641f3 --- /dev/null +++ b/tools/ddcprobe/.cvsignore @@ -0,0 +1 @@ +ddcxinfos diff --git a/tools/ddcprobe/Makefile b/tools/ddcprobe/Makefile new file mode 100644 index 000000000..e2bf84011 --- /dev/null +++ b/tools/ddcprobe/Makefile @@ -0,0 +1,18 @@ +CFLAGS=-Wall -O # -g -DDEBUG +TARGETS=ddcxinfos + +ddcxinfos: lrmi.o vesamode.o vbe.o ddcxinfos.o + +libvbe.a: lrmi.o vesamode.o vbe.o + $(AR) cru $@ $^ + +#install: $(DESTDIR)/usr/include/vbe.h $(DESTDIR)/usr/lib/libvbe.a + +$(DESTDIR)/usr/include/vbe.h: + install -m 644 vbe.h $(DESTDIR)/usr/include/vbe.h + +$(DESTDIR)/usr/lib/libvbe.a: + install -m 644 libvbe.a $(DESTDIR)/usr/lib/libvbe.a + +clean: + $(RM) $(TARGETS) *.o core diff --git a/tools/ddcprobe/ddcxinfos.c b/tools/ddcprobe/ddcxinfos.c new file mode 100644 index 000000000..439bdc75b --- /dev/null +++ b/tools/ddcprobe/ddcxinfos.c @@ -0,0 +1,66 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "vbe.h" +#include "vesamode.h" +#ident "$Id$" + +#define SQR(x) ((x) * (x)) + +int main(int argc, char **argv) +{ + int i, j; + u_int16_t *mode_list; + unsigned char hmin, hmax, vmin, vmax; + struct vbe_info *vbe_info; + struct vbe_edid1_info *edid; + struct vbe_modeline *modelines; + + + if ((vbe_info = vbe_get_vbe_info()) == NULL) return 1; + + printf("%dKB of video ram\n", vbe_info->memory_size * 64); + + /* List supported standard modes. */ + for (mode_list = vbe_info->mode_list.list; *mode_list != 0xffff; mode_list++) + for (i = 0; known_vesa_modes[i].x; i++) + if (known_vesa_modes[i].number == *mode_list) + printf("%d %d %d\n", + known_vesa_modes[i].colors, + known_vesa_modes[i].x, + known_vesa_modes[i].y + ); + printf("\n"); + + if ((edid = vbe_get_edid_info()) == NULL) return 0; + if (edid->version == 255 && edid->revision == 255) return 0; + + vbe_get_edid_ranges(edid, &hmin, &hmax, &vmin, &vmax); + modelines = vbe_get_edid_modelines(edid); + + printf(hmin ? "%d-%d\n" : "\n", hmin, hmax); + printf(vmin ? "%d-%d\n" : "\n", vmin, vmax); + + { + double size = sqrt(SQR(edid->max_size_horizontal) + + SQR(edid->max_size_vertical)) / 2.54; + printf(size ? "%3.2f inches monitor (truly %3.2f')\n" : "\n", size * 1.08, size); + } + + for(j=0; modelines && (modelines[j].refresh != 0); j++){ + printf("# %dx%d, %1.1f%sHz", + modelines[j].width, + modelines[j].height, + modelines[j].refresh, + modelines[j].interlaced?"i":"" + ); + if(modelines[j].modeline) { + printf("; hfreq=%f, vfreq=%f\n%s\n", + modelines[j].hfreq, + modelines[j].vfreq, + modelines[j].modeline); + } else printf("\n"); + } + return 0; +} diff --git a/tools/ddcprobe/lrmi.c b/tools/ddcprobe/lrmi.c new file mode 100644 index 000000000..ca8af0a1e --- /dev/null +++ b/tools/ddcprobe/lrmi.c @@ -0,0 +1,911 @@ +/* +Linux Real Mode Interface - A library of DPMI-like functions for Linux. + +Copyright (C) 1998 by Josh Vanderhoof + +You are free to distribute and modify this file, as long as you +do not remove this copyright notice and clearly label modified +versions as being modified. + +This software has NO WARRANTY. Use it at your own risk. +*/ + +#include <stdio.h> +#include <string.h> +#include <sys/io.h> +#include <asm/vm86.h> + +#ifdef USE_LIBC_VM86 +#include <sys/vm86.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> + +#include "lrmi.h" + +#define REAL_MEM_BASE ((void *)0x10000) +#define REAL_MEM_SIZE 0x10000 +#define REAL_MEM_BLOCKS 0x100 + +struct mem_block + { + unsigned int size : 20; + unsigned int free : 1; + }; + +static struct + { + int ready; + int count; + struct mem_block blocks[REAL_MEM_BLOCKS]; + } mem_info = { 0 }; + +static int +real_mem_init(void) + { + void *m; + int fd_zero; + + if (mem_info.ready) + return 1; + + fd_zero = open("/dev/zero", O_RDONLY); + if (fd_zero == -1) + { + perror("open /dev/zero"); + return 0; + } + + m = mmap((void *)REAL_MEM_BASE, REAL_MEM_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, fd_zero, 0); + + if (m == (void *)-1) + { + perror("mmap /dev/zero"); + close(fd_zero); + return 0; + } + + mem_info.ready = 1; + mem_info.count = 1; + mem_info.blocks[0].size = REAL_MEM_SIZE; + mem_info.blocks[0].free = 1; + + return 1; + } + + +static void +insert_block(int i) + { + memmove( + mem_info.blocks + i + 1, + mem_info.blocks + i, + (mem_info.count - i) * sizeof(struct mem_block)); + + mem_info.count++; + } + +static void +delete_block(int i) + { + mem_info.count--; + + memmove( + mem_info.blocks + i, + mem_info.blocks + i + 1, + (mem_info.count - i) * sizeof(struct mem_block)); + } + +void * +LRMI_alloc_real(int size) + { + int i; + char *r = (char *)REAL_MEM_BASE; + + if (!mem_info.ready) + return NULL; + + if (mem_info.count == REAL_MEM_BLOCKS) + return NULL; + + size = (size + 15) & ~15; + + for (i = 0; i < mem_info.count; i++) + { + if (mem_info.blocks[i].free && size < mem_info.blocks[i].size) + { + insert_block(i); + + mem_info.blocks[i].size = size; + mem_info.blocks[i].free = 0; + mem_info.blocks[i + 1].size -= size; + + return (void *)r; + } + + r += mem_info.blocks[i].size; + } + + return NULL; + } + + +void +LRMI_free_real(void *m) + { + int i; + char *r = (char *)REAL_MEM_BASE; + + if (!mem_info.ready) + return; + + i = 0; + while (m != (void *)r) + { + r += mem_info.blocks[i].size; + i++; + if (i == mem_info.count) + return; + } + + mem_info.blocks[i].free = 1; + + if (i + 1 < mem_info.count && mem_info.blocks[i + 1].free) + { + mem_info.blocks[i].size += mem_info.blocks[i + 1].size; + delete_block(i + 1); + } + + if (i - 1 >= 0 && mem_info.blocks[i - 1].free) + { + mem_info.blocks[i - 1].size += mem_info.blocks[i].size; + delete_block(i); + } + } + + +#define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK) +#define DEFAULT_STACK_SIZE 0x1000 +#define RETURN_TO_32_INT 255 + +static struct + { + int ready; + unsigned short ret_seg, ret_off; + unsigned short stack_seg, stack_off; + struct vm86_struct vm; + } context = { 0 }; + + +static inline void +set_bit(unsigned int bit, void *array) + { + unsigned char *a = array; + + a[bit / 8] |= (1 << (bit % 8)); + } + + +static inline unsigned int +get_int_seg(int i) + { + return *(unsigned short *)(i * 4 + 2); + } + + +static inline unsigned int +get_int_off(int i) + { + return *(unsigned short *)(i * 4); + } + + +static inline void +pushw(unsigned short i) + { + struct vm86_regs *r = &context.vm.regs; + r->esp -= 2; + *(unsigned short *)(((unsigned int)r->ss << 4) + r->esp) = i; + } + + +int +LRMI_init(void) + { + void *m; + int fd_mem; + + if (context.ready) + return 1; + + if (!real_mem_init()) + return 0; + + /* + Map the Interrupt Vectors (0x0 - 0x400) + BIOS data (0x400 - 0x502) + and the ROM (0xa0000 - 0x100000) + */ + fd_mem = open("/dev/mem", O_RDWR); + + if (fd_mem == -1) + { + perror("open /dev/mem"); + return 0; + } + + m = mmap((void *)0, 0x502, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, fd_mem, 0); + + if (m == (void *)-1) + { + perror("mmap /dev/mem"); + return 0; + } + + m = mmap((void *)0xa0000, 0x100000 - 0xa0000, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_SHARED, fd_mem, 0xa0000); + + if (m == (void *)-1) + { + perror("mmap /dev/mem"); + return 0; + } + + + /* + Allocate a stack + */ + m = LRMI_alloc_real(DEFAULT_STACK_SIZE); + + context.stack_seg = (unsigned int)m >> 4; + context.stack_off = DEFAULT_STACK_SIZE; + + /* + Allocate the return to 32 bit routine + */ + m = LRMI_alloc_real(2); + + context.ret_seg = (unsigned int)m >> 4; + context.ret_off = (unsigned int)m & 0xf; + + ((unsigned char *)m)[0] = 0xcd; /* int opcode */ + ((unsigned char *)m)[1] = RETURN_TO_32_INT; + + memset(&context.vm, 0, sizeof(context.vm)); + + /* + Enable kernel emulation of all ints except RETURN_TO_32_INT + */ + memset(&context.vm.int_revectored, 0, sizeof(context.vm.int_revectored)); + set_bit(RETURN_TO_32_INT, &context.vm.int_revectored); + + context.ready = 1; + + return 1; + } + + +static void +set_regs(struct LRMI_regs *r) + { + context.vm.regs.edi = r->edi; + context.vm.regs.esi = r->esi; + context.vm.regs.ebp = r->ebp; + context.vm.regs.ebx = r->ebx; + context.vm.regs.edx = r->edx; + context.vm.regs.ecx = r->ecx; + context.vm.regs.eax = r->eax; + context.vm.regs.eflags = DEFAULT_VM86_FLAGS; + context.vm.regs.es = r->es; + context.vm.regs.ds = r->ds; + context.vm.regs.fs = r->fs; + context.vm.regs.gs = r->gs; + } + + +static void +get_regs(struct LRMI_regs *r) + { + r->edi = context.vm.regs.edi; + r->esi = context.vm.regs.esi; + r->ebp = context.vm.regs.ebp; + r->ebx = context.vm.regs.ebx; + r->edx = context.vm.regs.edx; + r->ecx = context.vm.regs.ecx; + r->eax = context.vm.regs.eax; + r->flags = context.vm.regs.eflags; + r->es = context.vm.regs.es; + r->ds = context.vm.regs.ds; + r->fs = context.vm.regs.fs; + r->gs = context.vm.regs.gs; + } + +#define DIRECTION_FLAG (1 << 10) + +static void +em_ins(int size) + { + unsigned int edx, edi; + + edx = context.vm.regs.edx & 0xffff; + edi = context.vm.regs.edi & 0xffff; + edi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; insl; cld" + : "=D" (edi) : "d" (edx), "0" (edi)); + else if (size == 2) + asm volatile ("std; insw; cld" + : "=D" (edi) : "d" (edx), "0" (edi)); + else + asm volatile ("std; insb; cld" + : "=D" (edi) : "d" (edx), "0" (edi)); + } + else + { + if (size == 4) + asm volatile ("cld; insl" + : "=D" (edi) : "d" (edx), "0" (edi)); + else if (size == 2) + asm volatile ("cld; insw" + : "=D" (edi) : "d" (edx), "0" (edi)); + else + asm volatile ("cld; insb" + : "=D" (edi) : "d" (edx), "0" (edi)); + } + + edi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.edi &= 0xffff0000; + context.vm.regs.edi |= edi & 0xffff; + } + +static void +em_rep_ins(int size) + { + unsigned int ecx, edx, edi; + + ecx = context.vm.regs.ecx & 0xffff; + edx = context.vm.regs.edx & 0xffff; + edi = context.vm.regs.edi & 0xffff; + edi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; rep; insl; cld" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else if (size == 2) + asm volatile ("std; rep; insw; cld" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else + asm volatile ("std; rep; insb; cld" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + } + else + { + if (size == 4) + asm volatile ("cld; rep; insl" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else if (size == 2) + asm volatile ("cld; rep; insw" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else + asm volatile ("cld; rep; insb" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + } + + edi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.edi &= 0xffff0000; + context.vm.regs.edi |= edi & 0xffff; + + context.vm.regs.ecx &= 0xffff0000; + context.vm.regs.ecx |= ecx & 0xffff; + } + +static void +em_outs(int size) + { + unsigned int edx, esi; + + edx = context.vm.regs.edx & 0xffff; + esi = context.vm.regs.esi & 0xffff; + esi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; outsl; cld" + : "=S" (esi) : "d" (edx), "0" (esi)); + else if (size == 2) + asm volatile ("std; outsw; cld" + : "=S" (esi) : "d" (edx), "0" (esi)); + else + asm volatile ("std; outsb; cld" + : "=S" (esi) : "d" (edx), "0" (esi)); + } + else + { + if (size == 4) + asm volatile ("cld; outsl" + : "=S" (esi) : "d" (edx), "0" (esi)); + else if (size == 2) + asm volatile ("cld; outsw" + : "=S" (esi) : "d" (edx), "0" (esi)); + else + asm volatile ("cld; outsb" + : "=S" (esi) : "d" (edx), "0" (esi)); + } + + esi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.esi &= 0xffff0000; + context.vm.regs.esi |= esi & 0xffff; + } + +static void +em_rep_outs(int size) + { + unsigned int ecx, edx, esi; + + ecx = context.vm.regs.ecx & 0xffff; + edx = context.vm.regs.edx & 0xffff; + esi = context.vm.regs.esi & 0xffff; + esi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; rep; outsl; cld" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else if (size == 2) + asm volatile ("std; rep; outsw; cld" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else + asm volatile ("std; rep; outsb; cld" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + } + else + { + if (size == 4) + asm volatile ("cld; rep; outsl" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else if (size == 2) + asm volatile ("cld; rep; outsw" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else + asm volatile ("cld; rep; outsb" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + } + + esi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.esi &= 0xffff0000; + context.vm.regs.esi |= esi & 0xffff; + + context.vm.regs.ecx &= 0xffff0000; + context.vm.regs.ecx |= ecx & 0xffff; + } + +static void +em_inbl(unsigned char literal) + { + context.vm.regs.eax = inb(literal) & 0xff; + } + +static void +em_inb(void) + { + asm volatile ("inb (%w1), %b0" + : "=a" (context.vm.regs.eax) + : "d" (context.vm.regs.edx), "0" (context.vm.regs.eax)); + } + +static void +em_inw(void) + { + asm volatile ("inw (%w1), %w0" + : "=a" (context.vm.regs.eax) + : "d" (context.vm.regs.edx), "0" (context.vm.regs.eax)); + } + +static void +em_inl(void) + { + asm volatile ("inl (%w1), %0" + : "=a" (context.vm.regs.eax) + : "d" (context.vm.regs.edx)); + } + +static void +em_outbl(unsigned char literal) + { + outb(context.vm.regs.eax & 0xff, literal); + } + +static void +em_outb(void) + { + asm volatile ("outb %b0, (%w1)" + : : "a" (context.vm.regs.eax), + "d" (context.vm.regs.edx)); + } + +static void +em_outw(void) + { + asm volatile ("outw %w0, (%w1)" + : : "a" (context.vm.regs.eax), + "d" (context.vm.regs.edx)); + } + +static void +em_outl(void) + { + asm volatile ("outl %0, (%w1)" + : : "a" (context.vm.regs.eax), + "d" (context.vm.regs.edx)); + } + +static int +emulate(void) + { + unsigned char *insn; + struct + { + unsigned int size : 1; + unsigned int rep : 1; + } prefix = { 0, 0 }; + int i = 0; + + insn = (unsigned char *)((unsigned int)context.vm.regs.cs << 4); + insn += context.vm.regs.eip; + + while (1) + { + if (insn[i] == 0x66) + { + prefix.size = 1 - prefix.size; + i++; + } + else if (insn[i] == 0xf3) + { + prefix.rep = 1; + i++; + } + else if (insn[i] == 0xf0 || insn[i] == 0xf2 + || insn[i] == 0x26 || insn[i] == 0x2e + || insn[i] == 0x36 || insn[i] == 0x3e + || insn[i] == 0x64 || insn[i] == 0x65 + || insn[i] == 0x67) + { + /* these prefixes are just ignored */ + i++; + } + else if (insn[i] == 0x6c) + { + if (prefix.rep) + em_rep_ins(1); + else + em_ins(1); + i++; + break; + } + else if (insn[i] == 0x6d) + { + if (prefix.rep) + { + if (prefix.size) + em_rep_ins(4); + else + em_rep_ins(2); + } + else + { + if (prefix.size) + em_ins(4); + else + em_ins(2); + } + i++; + break; + } + else if (insn[i] == 0x6e) + { + if (prefix.rep) + em_rep_outs(1); + else + em_outs(1); + i++; + break; + } + else if (insn[i] == 0x6f) + { + if (prefix.rep) + { + if (prefix.size) + em_rep_outs(4); + else + em_rep_outs(2); + } + else + { + if (prefix.size) + em_outs(4); + else + em_outs(2); + } + i++; + break; + } + else if (insn[i] == 0xe4) + { + em_inbl(insn[i + 1]); + i += 2; + break; + } + else if (insn[i] == 0xe6) + { + em_outbl(insn[i + 1]); + i += 2; + break; + } + else if (insn[i] == 0xec) + { + em_inb(); + i++; + break; + } + else if (insn[i] == 0xed) + { + if (prefix.size) + em_inl(); + else + em_inw(); + i++; + break; + } + else if (insn[i] == 0xee) + { + em_outb(); + i++; + break; + } + else if (insn[i] == 0xef) + { + if (prefix.size) + em_outl(); + else + em_outw(); + + i++; + break; + } + else + return 0; + } + + context.vm.regs.eip += i; + return 1; + } + + +/* + I don't know how to make sure I get the right vm86() from libc. + The one I want is syscall # 113 (vm86old() in libc 5, vm86() in glibc) + which should be declared as "int vm86(struct vm86_struct *);" in + <sys/vm86.h>. + + This just does syscall 113 with inline asm, which should work + for both libc's (I hope). +*/ +#if !defined(USE_LIBC_VM86) +static int +lrmi_vm86(struct vm86_struct *vm) + { + int r; +#ifdef __PIC__ + asm volatile ( + "pushl %%ebx\n\t" + "movl %2, %%ebx\n\t" + "int $0x80\n\t" + "popl %%ebx" + : "=a" (r) + : "0" (113), "r" (vm)); +#else + asm volatile ( + "int $0x80" + : "=a" (r) + : "0" (113), "b" (vm)); +#endif + return r; + } +#else +#define lrmi_vm86 vm86 +#endif + + +static void +debug_info(int vret) + { + int i; + unsigned char *p; + + fputs("vm86() failed\n", stderr); + fprintf(stderr, "return = 0x%x\n", vret); + fprintf(stderr, "eax = 0x%08lx\n", context.vm.regs.eax); + fprintf(stderr, "ebx = 0x%08lx\n", context.vm.regs.ebx); + fprintf(stderr, "ecx = 0x%08lx\n", context.vm.regs.ecx); + fprintf(stderr, "edx = 0x%08lx\n", context.vm.regs.edx); + fprintf(stderr, "esi = 0x%08lx\n", context.vm.regs.esi); + fprintf(stderr, "edi = 0x%08lx\n", context.vm.regs.edi); + fprintf(stderr, "ebp = 0x%08lx\n", context.vm.regs.ebp); + fprintf(stderr, "eip = 0x%08lx\n", context.vm.regs.eip); + fprintf(stderr, "cs = 0x%04x\n", context.vm.regs.cs); + fprintf(stderr, "esp = 0x%08lx\n", context.vm.regs.esp); + fprintf(stderr, "ss = 0x%04x\n", context.vm.regs.ss); + fprintf(stderr, "ds = 0x%04x\n", context.vm.regs.ds); + fprintf(stderr, "es = 0x%04x\n", context.vm.regs.es); + fprintf(stderr, "fs = 0x%04x\n", context.vm.regs.fs); + fprintf(stderr, "gs = 0x%04x\n", context.vm.regs.gs); + fprintf(stderr, "eflags = 0x%08lx\n", context.vm.regs.eflags); + + fputs("cs:ip = [ ", stderr); + + p = (unsigned char *)((context.vm.regs.cs << 4) + (context.vm.regs.eip & 0xffff)); + + for (i = 0; i < 16; ++i) + fprintf(stderr, "%02x ", (unsigned int)p[i]); + + fputs("]\n", stderr); + } + + +static int +run_vm86(void) + { + unsigned int vret; + + while (1) + { + vret = lrmi_vm86(&context.vm); + + if (VM86_TYPE(vret) == VM86_INTx) + { + unsigned int v = VM86_ARG(vret); + + if (v == RETURN_TO_32_INT) + return 1; + + pushw(context.vm.regs.eflags); + pushw(context.vm.regs.cs); + pushw(context.vm.regs.eip); + + context.vm.regs.cs = get_int_seg(v); + context.vm.regs.eip = get_int_off(v); + context.vm.regs.eflags &= ~(VIF_MASK | TF_MASK); + + continue; + } + + if (VM86_TYPE(vret) != VM86_UNKNOWN) + break; + + if (!emulate()) + break; + } + +#ifdef ORIGINAL_LRMI_CODE_THAT_GOT_IFDEFED_OUT + debug_info(vret); +#endif + return 0; + } + + +int +LRMI_call(struct LRMI_regs *r) + { + unsigned int vret; + + memset(&context.vm.regs, 0, sizeof(context.vm.regs)); + + set_regs(r); + + context.vm.regs.cs = r->cs; + context.vm.regs.eip = r->ip; + + if (r->ss == 0 && r->sp == 0) + { + context.vm.regs.ss = context.stack_seg; + context.vm.regs.esp = context.stack_off; + } + else + { + context.vm.regs.ss = r->ss; + context.vm.regs.esp = r->sp; + } + + pushw(context.ret_seg); + pushw(context.ret_off); + + vret = run_vm86(); + + get_regs(r); + + return vret; + } + + +int +LRMI_int(int i, struct LRMI_regs *r) + { + unsigned int vret; + unsigned int seg, off; + + seg = get_int_seg(i); + off = get_int_off(i); + + /* + If the interrupt is in regular memory, it's probably + still pointing at a dos TSR (which is now gone). + */ + if (seg < 0xa000 || (seg << 4) + off >= 0x100000) + { +#ifdef ORIGINAL_LRMI_CODE_THAT_GOT_IFDEFED_OUT + fprintf(stderr, "Int 0x%x is not in rom (%04x:%04x)\n", i, seg, off); +#endif + return 0; + } + + memset(&context.vm.regs, 0, sizeof(context.vm.regs)); + + set_regs(r); + + context.vm.regs.cs = seg; + context.vm.regs.eip = off; + + if (r->ss == 0 && r->sp == 0) + { + context.vm.regs.ss = context.stack_seg; + context.vm.regs.esp = context.stack_off; + } + else + { + context.vm.regs.ss = r->ss; + context.vm.regs.esp = r->sp; + } + + pushw(DEFAULT_VM86_FLAGS); + pushw(context.ret_seg); + pushw(context.ret_off); + + vret = run_vm86(); + + get_regs(r); + + return vret; + } + diff --git a/tools/ddcprobe/lrmi.h b/tools/ddcprobe/lrmi.h new file mode 100644 index 000000000..c9c186365 --- /dev/null +++ b/tools/ddcprobe/lrmi.h @@ -0,0 +1,85 @@ +/* +Linux Real Mode Interface - A library of DPMI-like functions for Linux. + +Copyright (C) 1998 by Josh Vanderhoof + +You are free to distribute and modify this file, as long as you +do not remove this copyright notice and clearly label modified +versions as being modified. + +This software has NO WARRANTY. Use it at your own risk. +*/ + +#ifndef LRMI_H +#define LRMI_H + +struct LRMI_regs + { + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int reserved; + unsigned int ebx; + unsigned int edx; + unsigned int ecx; + unsigned int eax; + unsigned short int flags; + unsigned short int es; + unsigned short int ds; + unsigned short int fs; + unsigned short int gs; + unsigned short int ip; + unsigned short int cs; + unsigned short int sp; + unsigned short int ss; + }; + + +#ifndef LRMI_PREFIX +#define LRMI_PREFIX LRMI_ +#endif + +#define LRMI_CONCAT2(a, b) a ## b +#define LRMI_CONCAT(a, b) LRMI_CONCAT2(a, b) +#define LRMI_MAKENAME(a) LRMI_CONCAT(LRMI_PREFIX, a) + +/* + Initialize + returns 1 if sucessful, 0 for failure +*/ +#define LRMI_init LRMI_MAKENAME(init) +int +LRMI_init(void); + +/* + Simulate a 16 bit far call + returns 1 if sucessful, 0 for failure +*/ +#define LRMI_call LRMI_MAKENAME(call) +int +LRMI_call(struct LRMI_regs *r); + +/* + Simulate a 16 bit interrupt + returns 1 if sucessful, 0 for failure +*/ +#define LRMI_int LRMI_MAKENAME(int) +int +LRMI_int(int interrupt, struct LRMI_regs *r); + +/* + Allocate real mode memory + The returned block is paragraph (16 byte) aligned +*/ +#define LRMI_alloc_real LRMI_MAKENAME(alloc_real) +void * +LRMI_alloc_real(int size); + +/* + Free real mode memory +*/ +#define LRMI_free_real LRMI_MAKENAME(free_real) +void +LRMI_free_real(void *m); + +#endif diff --git a/tools/ddcprobe/vbe.c b/tools/ddcprobe/vbe.c new file mode 100644 index 000000000..a67e56d0b --- /dev/null +++ b/tools/ddcprobe/vbe.c @@ -0,0 +1,709 @@ +#include <sys/types.h> +#include <sys/io.h> +#include <sys/mman.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <ctype.h> +#include "lrmi.h" +#include "vesamode.h" +#include "vbe.h" +#ident "$Id$" + +/* Return information about a particular video mode. */ +struct vbe_mode_info *vbe_get_mode_info(u_int16_t mode) +{ + struct LRMI_regs regs; + char *mem; + struct vbe_mode_info *ret = NULL; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return NULL; + } + + /* Allocate a chunk of memory. */ + mem = LRMI_alloc_real(sizeof(struct vbe_mode_info)); + if(mem == NULL) { + return NULL; + } + memset(mem, 0, sizeof(struct vbe_mode_info)); + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f01; + regs.ecx = mode; + regs.es = ((u_int32_t)mem) >> 4; + regs.edi = ((u_int32_t)mem) & 0x0f; + + /* Do it. */ + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + LRMI_free_real(mem); + return NULL; + } + + /* Check for successful return. */ + if((regs.eax & 0xffff) != 0x004f) { + LRMI_free_real(mem); + return NULL; + } + + /* Get memory for return. */ + ret = malloc(sizeof(struct vbe_mode_info)); + if(ret == NULL) { + LRMI_free_real(mem); + return NULL; + } + + /* Copy the buffer for return. */ + memcpy(ret, mem, sizeof(struct vbe_mode_info)); + + /* Clean up and return. */ + LRMI_free_real(mem); + return ret; +} + +/* Get VBE info. */ +struct vbe_info *vbe_get_vbe_info() +{ + struct LRMI_regs regs; + unsigned char *mem; + struct vbe_info *ret = NULL; + int i; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return NULL; + } + + /* Allocate a chunk of memory. */ + mem = LRMI_alloc_real(sizeof(struct vbe_mode_info)); + if(mem == NULL) { + return NULL; + } + memset(mem, 0, sizeof(struct vbe_mode_info)); + + /* Set up registers for the interrupt call. */ + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f00; + regs.es = ((u_int32_t)mem) >> 4; + regs.edi = ((u_int32_t)mem) & 0x0f; + memcpy(mem, "VBE2", 4); + + /* Do it. */ + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + LRMI_free_real(mem); + return NULL; + } + + /* Check for successful return code. */ + if((regs.eax & 0xffff) != 0x004f) { + LRMI_free_real(mem); + return NULL; + } + + /* Get memory to return the information. */ + ret = malloc(sizeof(struct vbe_info)); + if(ret == NULL) { + LRMI_free_real(mem); + return NULL; + } + memcpy(ret, mem, sizeof(struct vbe_info)); + + /* Set up pointers to usable memory. */ + ret->mode_list.list = (u_int16_t*) ((ret->mode_list.addr.seg << 4) + + (ret->mode_list.addr.ofs)); + ret->oem_name.string = (char*) ((ret->oem_name.addr.seg << 4) + + (ret->oem_name.addr.ofs)); + + /* Snip, snip. */ + mem = strdup(ret->oem_name.string); /* leak */ + while(((i = strlen(mem)) > 0) && isspace(mem[i - 1])) { + mem[i - 1] = '\0'; + } + ret->oem_name.string = mem; + + /* Set up pointers for VESA 3.0+ strings. */ + if(ret->version[1] >= 3) { + + /* Vendor name. */ + ret->vendor_name.string = (char*) + ((ret->vendor_name.addr.seg << 4) + + (ret->vendor_name.addr.ofs)); + + mem = strdup(ret->vendor_name.string); /* leak */ + while(((i = strlen(mem)) > 0) && isspace(mem[i - 1])) { + mem[i - 1] = '\0'; + } + ret->vendor_name.string = mem; + + /* Product name. */ + ret->product_name.string = (char*) + ((ret->product_name.addr.seg << 4) + + (ret->product_name.addr.ofs)); + + mem = strdup(ret->product_name.string); /* leak */ + while(((i = strlen(mem)) > 0) && isspace(mem[i - 1])) { + mem[i - 1] = '\0'; + } + ret->product_name.string = mem; + + /* Product revision. */ + ret->product_revision.string = (char*) + ((ret->product_revision.addr.seg << 4) + + (ret->product_revision.addr.ofs)); + + mem = strdup(ret->product_revision.string); /* leak */ + while(((i = strlen(mem)) > 0) && isspace(mem[i - 1])) { + mem[i - 1] = '\0'; + } + ret->product_revision.string = mem; + } + + /* Cleanup. */ + LRMI_free_real(mem); + return ret; +} + +/* Check if EDID queries are suorted. */ +int vbe_get_edid_supported() +{ + struct LRMI_regs regs; + int ret = 0; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return 0; + } + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f15; + regs.ebx = 0x0000; + regs.es = 0x3000; + regs.edi = 0x3000; + + /* Do it. */ + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + return 0; + } + + /* Check for successful return. */ + if((regs.eax & 0xff) == 0x4f) { + /* Supported. */ + ret = 1; + } else { + /* Not supported. */ + ret = 0; + } + + /* Clean up and return. */ + return ret; +} + +/* Get EDID info. */ +struct vbe_edid1_info *vbe_get_edid_info() +{ + struct LRMI_regs regs; + unsigned char *mem; + struct vbe_edid1_info *ret = NULL; + u_int16_t man; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return NULL; + } + + /* Allocate a chunk of memory. */ + mem = LRMI_alloc_real(sizeof(struct vbe_edid1_info)); + if(mem == NULL) { + return NULL; + } + memset(mem, 0, sizeof(struct vbe_edid1_info)); + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f15; + regs.ebx = 0x0001; + regs.es = ((u_int32_t)mem) >> 4; + regs.edi = ((u_int32_t)mem) & 0x0f; + + /* Do it. */ + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + LRMI_free_real(mem); + return NULL; + } + +#if 0 + /* Check for successful return. */ + if((regs.eax & 0xffff) != 0x004f) { + LRMI_free_real(mem); + return NULL; + } +#elseif + /* Check for successful return. */ + if((regs.eax & 0xff) != 0x4f) { + LRMI_free_real(mem); + return NULL; + } +#endif + + /* Get memory for return. */ + ret = malloc(sizeof(struct vbe_edid1_info)); + if(ret == NULL) { + LRMI_free_real(mem); + return NULL; + } + + /* Copy the buffer for return. */ + memcpy(ret, mem, sizeof(struct vbe_edid1_info)); + + memcpy(&man, &ret->manufacturer_name, 2); + man = ntohs(man); + memcpy(&ret->manufacturer_name, &man, 2); + + LRMI_free_real(mem); + return ret; +} + +/* Figure out what the current video mode is. */ +int32_t vbe_get_mode() +{ + struct LRMI_regs regs; + int32_t ret = -1; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return -1; + } + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f03; + + /* Do it. */ + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + return -1; + } + + /* Save the returned value. */ + if((regs.eax & 0xffff) == 0x004f) { + ret = regs.ebx & 0xffff; + } else { + ret = -1; + } + + /* Clean up and return. */ + return ret; +} + +/* Set the video mode. */ +void vbe_set_mode(u_int16_t mode) +{ + struct LRMI_regs regs; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return; + } + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f02; + regs.ebx = mode; + + /* Do it. */ + iopl(3); + ioperm(0, 0x400, 1); + LRMI_int(0x10, ®s); + + /* Return. */ + return; +} + +/* Just read ranges from the EDID. */ +void vbe_get_edid_ranges(struct vbe_edid1_info *edid, + unsigned char *hmin, unsigned char *hmax, + unsigned char *vmin, unsigned char *vmax) +{ + struct vbe_edid_monitor_descriptor *monitor; + int i; + + *hmin = *hmax = *vmin = *vmax = 0; + + for(i = 0; i < 4; i++) { + monitor = &edid->monitor_details.monitor_descriptor[i]; + if(monitor->type == vbe_edid_monitor_descriptor_range) { + *hmin = monitor->data.range_data.horizontal_min; + *hmax = monitor->data.range_data.horizontal_max; + *vmin = monitor->data.range_data.vertical_min; + *vmax = monitor->data.range_data.vertical_max; + } + } +} + +static int compare_vbe_modelines(const void *m1, const void *m2) +{ + const struct vbe_modeline *M1 = (const struct vbe_modeline*) m1; + const struct vbe_modeline *M2 = (const struct vbe_modeline*) m2; + if(M1->width < M2->width) return -1; + if(M1->width > M2->width) return 1; + return 0; +} + +struct vbe_modeline *vbe_get_edid_modelines() +{ + struct vbe_edid1_info *edid; + struct vbe_modeline *ret; + char buf[LINE_MAX]; + int modeline_count = 0, i, j; + + if((edid = vbe_get_edid_info()) == NULL) { + return NULL; + } + + memcpy(buf, &edid->established_timings, + sizeof(edid->established_timings)); + for(i = 0; i < (8 * sizeof(edid->established_timings)); i++) { + if(buf[i / 8] & (1 << (i % 8))) { + modeline_count++; + } + } + + /* Count the number of standard timings. */ + for(i = 0; i < 8; i++) { + int x, v; + x = edid->standard_timing[i].xresolution; + v = edid->standard_timing[i].vfreq; + if(((edid->standard_timing[i].xresolution & 0x01) != x) && + ((edid->standard_timing[i].vfreq & 0x01) != v)) { + modeline_count++; + } + } + + ret = malloc(sizeof(struct vbe_modeline) * (modeline_count + 1)); + if(ret == NULL) { + return NULL; + } + memset(ret, 0, sizeof(struct vbe_modeline) * (modeline_count + 1)); + + modeline_count = 0; + + /* Fill out established timings. */ + if(edid->established_timings.timing_720x400_70) { + ret[modeline_count].width = 720; + ret[modeline_count].height = 400; + ret[modeline_count].refresh = 70; + modeline_count++; + } + if(edid->established_timings.timing_720x400_88) { + ret[modeline_count].width = 720; + ret[modeline_count].height = 400; + ret[modeline_count].refresh = 88; + modeline_count++; + } + if(edid->established_timings.timing_640x480_60) { + ret[modeline_count].width = 640; + ret[modeline_count].height = 480; + ret[modeline_count].refresh = 60; + modeline_count++; + } + if(edid->established_timings.timing_640x480_67) { + ret[modeline_count].width = 640; + ret[modeline_count].height = 480; + ret[modeline_count].refresh = 67; + modeline_count++; + } + if(edid->established_timings.timing_640x480_72) { + ret[modeline_count].width = 640; + ret[modeline_count].height = 480; + ret[modeline_count].refresh = 72; + modeline_count++; + } + if(edid->established_timings.timing_640x480_75) { + ret[modeline_count].width = 640; + ret[modeline_count].height = 480; + ret[modeline_count].refresh = 75; + modeline_count++; + } + if(edid->established_timings.timing_800x600_56) { + ret[modeline_count].width = 800; + ret[modeline_count].height = 600; + ret[modeline_count].refresh = 56; + modeline_count++; + } + if(edid->established_timings.timing_800x600_60) { + ret[modeline_count].width = 800; + ret[modeline_count].height = 600; + ret[modeline_count].refresh = 60; + modeline_count++; + } + if(edid->established_timings.timing_800x600_72) { + ret[modeline_count].width = 800; + ret[modeline_count].height = 600; + ret[modeline_count].refresh = 72; + modeline_count++; + } + if(edid->established_timings.timing_800x600_75) { + ret[modeline_count].width = 800; + ret[modeline_count].height = 600; + ret[modeline_count].refresh = 75; + modeline_count++; + } + if(edid->established_timings.timing_832x624_75) { + ret[modeline_count].width = 832; + ret[modeline_count].height = 624; + ret[modeline_count].refresh = 75; + modeline_count++; + } + if(edid->established_timings.timing_1024x768_87i) { + ret[modeline_count].width = 1024; + ret[modeline_count].height = 768; + ret[modeline_count].refresh = 87; + ret[modeline_count].interlaced = 1; + modeline_count++; + } + if(edid->established_timings.timing_1024x768_60){ + ret[modeline_count].width = 1024; + ret[modeline_count].height = 768; + ret[modeline_count].refresh = 60; + modeline_count++; + } + if(edid->established_timings.timing_1024x768_70){ + ret[modeline_count].width = 1024; + ret[modeline_count].height = 768; + ret[modeline_count].refresh = 70; + modeline_count++; + } + if(edid->established_timings.timing_1024x768_75){ + ret[modeline_count].width = 1024; + ret[modeline_count].height = 768; + ret[modeline_count].refresh = 75; + modeline_count++; + } + if(edid->established_timings.timing_1280x1024_75) { + ret[modeline_count].width = 1280; + ret[modeline_count].height = 1024; + ret[modeline_count].refresh = 75; + modeline_count++; + } + + /* Add in standard timings. */ + for(i = 0; i < 8; i++) { + float aspect = 1; + int x, v; + x = edid->standard_timing[i].xresolution; + v = edid->standard_timing[i].vfreq; + if(((edid->standard_timing[i].xresolution & 0x01) != x) && + ((edid->standard_timing[i].vfreq & 0x01) != v)) { + switch(edid->standard_timing[i].aspect) { + case aspect_75: aspect = 0.7500; break; + case aspect_8: aspect = 0.8000; break; + case aspect_5625: aspect = 0.5625; break; + default: aspect = 1; break; + } + x = (edid->standard_timing[i].xresolution + 31) * 8; + ret[modeline_count].width = x; + ret[modeline_count].height = x * aspect; + ret[modeline_count].refresh = + edid->standard_timing[i].vfreq + 60; + modeline_count++; + } + } + + /* Now tack on any matching modelines. */ + for(i = 0; ret[i].refresh != 0; i++) { + struct vesa_timing_t *t = NULL; + for(j = 0; known_vesa_timings[j].refresh != 0; j++) { + t = &known_vesa_timings[j]; + if(ret[i].width == t->x) + if(ret[i].height == t->y) + if(ret[i].refresh == t->refresh) { + snprintf(buf, sizeof(buf), + "ModeLine \"%dx%d\"\t%6.2f " + "%4d %4d %4d %4d %4d %4d %4d %4d %s %s" + , t->x, t->y, t->dotclock, + t->timings[0], + t->timings[0] + t->timings[1], + t->timings[0] + t->timings[1] + + t->timings[2], + t->timings[0] + t->timings[1] + + t->timings[2] + t->timings[3], + t->timings[4], + t->timings[4] + t->timings[5], + t->timings[4] + t->timings[5] + + t->timings[6], + t->timings[4] + t->timings[5] + + t->timings[6] + t->timings[7], + t->hsync == hsync_pos ? + "+hsync" : "-hsync", + t->vsync == vsync_pos ? + "+vsync" : "-vsync"); + ret[i].modeline = strdup(buf); + ret[i].hfreq = t->hfreq; + ret[i].vfreq = t->vfreq; + } + } + } + + modeline_count = 0; + for(i = 0; ret[i].refresh != 0; i++) { + modeline_count++; + } + qsort(ret, modeline_count, sizeof(ret[0]), compare_vbe_modelines); + + return ret; +} + +const void *vbe_save_svga_state() +{ + struct LRMI_regs regs; + unsigned char *mem; + u_int16_t block_size; + void *data; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return NULL; + } + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f04; + regs.ecx = 0xffff; + regs.edx = 0; + + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + return NULL; + } + + if((regs.eax & 0xff) != 0x4f) { + fprintf(stderr, "Get SuperVGA Video State not supported.\n"); + return NULL; + } + + if((regs.eax & 0xffff) != 0x004f) { + fprintf(stderr, "Get SuperVGA Video State Info failed.\n"); + return NULL; + } + + block_size = 64 * (regs.ebx & 0xffff); + + /* Allocate a chunk of memory. */ + mem = LRMI_alloc_real(block_size); + if(mem == NULL) { + return NULL; + } + memset(mem, 0, sizeof(block_size)); + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f04; + regs.ecx = 0x000f; + regs.edx = 0x0001; + regs.es = ((u_int32_t)mem) >> 4; + regs.ebx = ((u_int32_t)mem) & 0x0f; + memset(mem, 0, block_size); + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + LRMI_free_real(mem); + return NULL; + } + + if((regs.eax & 0xffff) != 0x004f) { + fprintf(stderr, "Get SuperVGA Video State Save failed.\n"); + return NULL; + } + + data = malloc(block_size); + if(data == NULL) { + LRMI_free_real(mem); + return NULL; + } + + /* Clean up and return. */ + memcpy(data, mem, block_size); + LRMI_free_real(mem); + return data; +} + +void vbe_restore_svga_state(const void *state) +{ + struct LRMI_regs regs; + unsigned char *mem; + u_int16_t block_size; + + /* Initialize LRMI. */ + if(LRMI_init() == 0) { + return; + } + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f04; + regs.ecx = 0x000f; + regs.edx = 0; + + /* Find out how much memory we need. */ + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + return; + } + + if((regs.eax & 0xff) != 0x4f) { + fprintf(stderr, "Get SuperVGA Video State not supported.\n"); + return; + } + + if((regs.eax & 0xffff) != 0x004f) { + fprintf(stderr, "Get SuperVGA Video State Info failed.\n"); + return; + } + + block_size = 64 * (regs.ebx & 0xffff); + + /* Allocate a chunk of memory. */ + mem = LRMI_alloc_real(block_size); + if(mem == NULL) { + return; + } + memset(mem, 0, sizeof(block_size)); + + memset(®s, 0, sizeof(regs)); + regs.eax = 0x4f04; + regs.ecx = 0x000f; + regs.edx = 0x0002; + regs.es = 0x2000; + regs.ebx = 0x0000; + memcpy(mem, state, block_size); + + iopl(3); + ioperm(0, 0x400, 1); + + if(LRMI_int(0x10, ®s) == 0) { + LRMI_free_real(mem); + return; + } + + if((regs.eax & 0xffff) != 0x004f) { + fprintf(stderr, "Get SuperVGA Video State Restore failed.\n"); + return; + } +} diff --git a/tools/ddcprobe/vbe.h b/tools/ddcprobe/vbe.h new file mode 100644 index 000000000..338d3bd88 --- /dev/null +++ b/tools/ddcprobe/vbe.h @@ -0,0 +1,312 @@ +#ifndef vbe_h +#define vbe_h +#ident "$Id$" +#include <sys/types.h> + +/* Record returned by int 0x10, function 0x4f, subfunction 0x00. */ +struct vbe_info { + unsigned char signature[4]; + unsigned char version[2]; + union { + struct { + u_int16_t ofs; + u_int16_t seg; + } addr; + const char *string; + } oem_name; + u_int32_t capabilities; + union { + struct { + u_int16_t ofs; + u_int16_t seg; + } addr; + u_int16_t *list; + } mode_list; + u_int16_t memory_size; + /* VESA 3.0+ */ + u_int16_t vbe_revision; + union { + struct { + u_int16_t ofs; + u_int16_t seg; + } addr; + const char *string; + } vendor_name; + union { + struct { + u_int16_t ofs; + u_int16_t seg; + } addr; + const char *string; + } product_name; + union { + struct { + u_int16_t ofs; + u_int16_t seg; + } addr; + const char *string; + } product_revision; + char reserved1[222]; + char reserved2[256]; +} __attribute__ ((packed)); + +/* Stuff returned by int 0x10, function 0x4f, subfunction 0x01. */ +struct vbe_mode_info { + /* required for all VESA versions */ + struct { + /* VBE 1.0+ */ + u_int16_t supported: 1; + u_int16_t optional_info_available: 1; + u_int16_t bios_output_supported: 1; + u_int16_t color: 1; + u_int16_t graphics: 1; + /* VBE 2.0+ */ + u_int16_t not_vga_compatible: 1; + u_int16_t not_bank_switched: 1; + u_int16_t lfb: 1; + /* VBE 1.0+ */ + u_int16_t unknown: 1; + u_int16_t must_enable_directaccess_in_10: 1; + } mode_attributes; + struct { + unsigned char exists: 1; + unsigned char readable: 1; + unsigned char writeable: 1; + unsigned char reserved: 5; + } windowa_attributes, windowb_attributes; + u_int16_t window_granularity; + u_int16_t window_size; + u_int16_t windowa_start_segment, windowb_start_segment; + u_int16_t window_positioning_seg, window_positioning_ofs; + u_int16_t bytes_per_scanline; + /* optional for VESA 1.0/1.1, required for OEM modes */ + u_int16_t w, h; + unsigned char cell_width, cell_height; + unsigned char memory_planes; + unsigned char bpp; + unsigned char banks; + enum { + memory_model_text = 0, + memory_model_cga = 1, + memory_model_hgc = 2, + memory_model_ega16 = 3, + memory_model_packed_pixel = 4, + memory_model_sequ256 = 5, + memory_model_direct_color = 6, + memory_model_yuv = 7, + } memory_model: 8; + unsigned char bank_size; + unsigned char image_pages; + unsigned char reserved1; + /* required for VESA 1.2+ */ + unsigned char red_mask, red_field; + unsigned char green_mask, green_field; + unsigned char blue_mask, blue_field; + unsigned char reserved_mask, reserved_field; + unsigned char direct_color_mode_info; + /* VESA 2.0+ */ + u_int32_t linear_buffer_address; + u_int32_t offscreen_memory_address; + u_int16_t offscreen_memory_size; + unsigned char reserved2[206]; +} __attribute__ ((packed)); + +/* Modeline information used by XFree86. */ +struct vbe_modeline { + u_int16_t width, height; + unsigned char interlaced; + float refresh; + char *modeline; + float hfreq, vfreq, pixel_clock; +}; + +/* Aspect ratios used in EDID info. */ +enum vbe_edid_aspect { + aspect_unknown = 0, + aspect_75, + aspect_8, + aspect_5625, +}; + +/* Detailed timing information used in EDID v1.x */ +struct vbe_edid_detailed_timing { + u_int16_t pixel_clock; +#define VBE_EDID_DETAILED_TIMING_PIXEL_CLOCK(_x) \ + ((_x).pixel_clock * 10000) + unsigned char horizontal_active; + unsigned char horizontal_blanking; + unsigned char horizontal_active_hi: 4; + unsigned char horizontal_blanking_hi: 4; +#define VBE_EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(_x) \ + (((_x).horizontal_active_hi << 8) + (_x).horizontal_active) +#define VBE_EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(_x) \ + (((_x).horizontal_blanking_hi << 8) + (_x).horizontal_blanking) + unsigned char vertical_active; + unsigned char vertical_blanking; + unsigned char vertical_active_hi: 4; + unsigned char vertical_blanking_hi: 4; +#define VBE_EDID_DETAILED_TIMING_VERTICAL_ACTIVE(_x) \ + (((_x).vertical_active_hi << 8) + (_x).vertical_active) +#define VBE_EDID_DETAILED_TIMING_VERTICAL_BLANKING(_x) \ + (((_x).vertical_blanking_hi << 8) + (_x).vertical_blanking) + unsigned char hsync_offset; + unsigned char hsync_pulse_width; + unsigned char vsync_offset: 4; + unsigned char vsync_pulse_width: 4; + unsigned char hsync_offset_hi: 2; + unsigned char hsync_pulse_width_hi: 2; + unsigned char vsync_offset_hi: 2; + unsigned char vsync_pulse_width_hi: 2; +#define VBE_EDID_DETAILED_TIMING_HSYNC_OFFSET(_x) \ + (((_x).hsync_offset_hi << 8) + (_x).hsync_offset) +#define VBE_EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(_x) \ + (((_x).hsync_pulse_width_hi << 8) + (_x).hsync_pulse_width) +#define VBE_EDID_DETAILED_TIMING_VSYNC_OFFSET(_x) \ + (((_x).vsync_offset_hi << 4) + (_x).vsync_offset) +#define VBE_EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(_x) \ + (((_x).vsync_pulse_width_hi << 4) + (_x).vsync_pulse_width) + unsigned char himage_size; + unsigned char vimage_size; + unsigned char himage_size_hi: 4; + unsigned char vimage_size_hi: 4; +#define VBE_EDID_DETAILED_TIMING_HIMAGE_SIZE(_x) \ + (((_x).himage_size_hi << 8) + (_x).himage_size) +#define VBE_EDID_DETAILED_TIMING_VIMAGE_SIZE(_x) \ + (((_x).vimage_size_hi << 8) + (_x).vimage_size) + unsigned char hborder; + unsigned char vborder; + struct { + unsigned char interlaced: 1; + unsigned char stereo: 2; + unsigned char digital_composite: 2; + unsigned char variant: 2; + unsigned char zero: 1; + } flags __attribute__ ((packed)); +} __attribute__ ((packed)); + +enum { + vbe_edid_monitor_descriptor_serial = 0xff, + vbe_edid_monitor_descriptor_ascii = 0xfe, + vbe_edid_monitor_descriptor_range = 0xfd, + vbe_edid_monitor_descriptor_name = 0xfc, +} vbe_edid_monitor_descriptor_types; + +struct vbe_edid_monitor_descriptor { + u_int16_t zero_flag_1; + unsigned char zero_flag_2; + unsigned char type; + unsigned char zero_flag_3; + union { + char string[13]; + struct { + unsigned char vertical_min; + unsigned char vertical_max; + unsigned char horizontal_min; + unsigned char horizontal_max; + unsigned char pixel_clock_max; + unsigned char gtf_data[8]; + } range_data; + } data; +} __attribute__ ((packed)); + +struct vbe_edid1_info { + unsigned char header[8]; + struct { + u_int16_t char3: 5; + u_int16_t char2: 5; + u_int16_t char1: 5; + u_int16_t zero: 1; + } manufacturer_name __attribute__ ((packed)); + u_int16_t product_code; + u_int32_t serial_number; + unsigned char week; + unsigned char year; + unsigned char version; + unsigned char revision; + struct { + unsigned char separate_sync: 1; + unsigned char composite_sync: 1; + unsigned char sync_on_green: 1; + unsigned char unused: 2; + unsigned char voltage_level: 2; + unsigned char digital: 1; + } video_input_definition __attribute__ ((packed)); + unsigned char max_size_horizontal; + unsigned char max_size_vertical; + unsigned char gamma; + struct { + unsigned char unused1: 3; + unsigned char rgb: 1; + unsigned char unused2: 1; + unsigned char active_off: 1; + unsigned char suspend: 1; + unsigned char standby: 1; + } feature_support __attribute__ ((packed)); + unsigned char color_characteristics[10]; + struct { + unsigned char timing_720x400_70: 1; + unsigned char timing_720x400_88: 1; + unsigned char timing_640x480_60: 1; + unsigned char timing_640x480_67: 1; + unsigned char timing_640x480_72: 1; + unsigned char timing_640x480_75: 1; + unsigned char timing_800x600_56: 1; + unsigned char timing_800x600_60: 1; + unsigned char timing_800x600_72: 1; + unsigned char timing_800x600_75: 1; + unsigned char timing_832x624_75: 1; + unsigned char timing_1024x768_87i: 1; + unsigned char timing_1024x768_60: 1; + unsigned char timing_1024x768_70: 1; + unsigned char timing_1024x768_75: 1; + unsigned char timing_1280x1024_75: 1; + } established_timings __attribute__ ((packed)); + struct { + unsigned char timing_1152x870_75: 1; + unsigned char reserved: 7; + } manufacturer_timings __attribute__ ((packed)); + struct { + u_int16_t xresolution: 8; + u_int16_t vfreq: 6; + u_int16_t aspect: 2; + } standard_timing[8] __attribute__ ((packed)); + union { + struct vbe_edid_detailed_timing detailed_timing[4]; + struct vbe_edid_monitor_descriptor monitor_descriptor[4]; + } monitor_details __attribute__ ((packed)); + unsigned char extension_flag; + unsigned char checksum; + unsigned char padding[128]; +} __attribute__ ((packed)); + +#define VBE_LINEAR_FRAMEBUFFER 0x4000 + +/* Get VESA information. */ +struct vbe_info *vbe_get_vbe_info(); + +/* Get information about a particular video mode, bitwise or with + VBE_LINEAR_FRAMEBUFFER to check if LFB version is supported. */ +struct vbe_mode_info *vbe_get_mode_info(u_int16_t mode); + +/* Check if EDID reads are supported, and do them. */ +int vbe_get_edid_supported(); +struct vbe_edid1_info *vbe_get_edid_info(); + +/* Get the current video mode, -1 on error. */ +int32_t vbe_get_mode(); +/* Set a new video mode, bitwise or with VBE_LINEAR_FRAMEBUFFER. */ +void vbe_set_mode(u_int16_t mode); + +/* Save/restore the SVGA state. Call free() on the state record when done. */ +const void *vbe_save_svga_state(); +void vbe_restore_svga_state(const void *state); + +/* Get the ranges of values suitable for the attached monitor. */ +void vbe_get_edid_ranges(struct vbe_edid1_info *edid, + unsigned char *hmin, unsigned char *hmax, + unsigned char *vmin, unsigned char *vmax); + +/* Get a list of modelines that will work with this monitor. */ +struct vbe_modeline *vbe_get_edid_modelines(); + +#endif diff --git a/tools/ddcprobe/vesamode.c b/tools/ddcprobe/vesamode.c new file mode 100644 index 000000000..9d4c3797a --- /dev/null +++ b/tools/ddcprobe/vesamode.c @@ -0,0 +1,132 @@ +#include "vesamode.h" +#ident "$Id$" + +/* Known standard VESA modes. */ +struct vesa_mode_t known_vesa_modes[] = { + /* VESA 1.0/1.1 ? */ + {0x100, 640, 400, 256, "640x400x256"}, + {0x101, 640, 480, 256, "640x480x256"}, + {0x102, 800, 600, 16, "800x600x16"}, + {0x103, 800, 600, 256, "800x600x256"}, + {0x104, 1024, 768, 16, "1024x768x16"}, + {0x105, 1024, 768, 256, "1024x768x256"}, + {0x106, 1280, 1024, 16, "1280x1024x16"}, + {0x107, 1280, 1024, 256,"1280x1024x256"}, + {0x108, 80, 60, 16, "80x60 (text)"}, + {0x109, 132, 25, 16, "132x25 (text)"}, + {0x10a, 132, 43, 16, "132x43 (text)"}, + {0x10b, 132, 50, 16, "132x50 (text)"}, + {0x10c, 132, 60, 16, "132x60 (text)"}, + /* VESA 1.2+ */ + {0x10d, 320, 200, 32768, "320x200x32k"}, + {0x10e, 320, 200, 65536, "320x200x64k"}, + {0x10f, 320, 200, 16777216, "320x200x16m"}, + {0x110, 640, 480, 32768, "640x480x32k"}, + {0x111, 640, 480, 65536, "640x480x64k"}, + {0x112, 640, 480, 16777216, "640x480x16m"}, + {0x113, 800, 600, 32768, "800x600x32k"}, + {0x114, 800, 600, 65536, "800x600x64k"}, + {0x115, 800, 600, 16777216, "800x600x16m"}, + {0x116, 1024, 768, 32768, "1024x768x32k"}, + {0x117, 1024, 768, 65536, "1024x768x64k"}, + {0x118, 1024, 768, 16777216, "1024x768x16m"}, + {0x119, 1280, 1024, 32768, "1280x1024x32k"}, + {0x11a, 1280, 1024, 65536, "1280x1024x64k"}, + {0x11b, 1280, 1024, 16777216, "1280x1024x16m"}, + /* VESA 2.0+ */ + {0x120, 1600, 1200, 256, "1600x1200x256"}, + {0x121, 1600, 1200, 32768, "1600x1200x32k"}, + {0x122, 1600, 1200, 65536, "1600x1200x64k"}, + { 0, 0, 0, 0, ""}, +}; + +struct vesa_timing_t known_vesa_timings[] = { + /* Source: VESA Monitor Timing Specifications 1.0 rev 0.8 */ + { 640, 350, 85, 31.500, { 640, 32, 64, 96, 350,32, 3, 60}, + hsync_pos, vsync_neg, 37.861, 85.080}, + + { 640, 400, 85, 31.500, { 640, 32, 64, 96, 400, 1, 3, 41}, + hsync_neg, vsync_pos, 37.861, 85.080}, + + { 720, 400, 85, 35.500, { 720, 36, 72, 108, 400, 1, 3, 42}, + hsync_neg, vsync_pos, 37.861, 85.080}, + + { 640, 480, 60, 25.175, { 640, 8, 96, 40, 480, 2, 2, 25}, + hsync_neg, vsync_neg, 31.469, 59.940}, + { 640, 480, 72, 31.500, { 640, 16, 40, 120, 480, 1, 3, 20}, + hsync_neg, vsync_neg, 37.861, 72.809}, + { 640, 480, 75, 31.500, { 640, 16, 64, 120, 480, 1, 3, 16}, + hsync_neg, vsync_neg, 37.500, 75.000}, + { 640, 480, 85, 36.000, { 640, 56, 56, 80, 480, 1, 3, 25}, + hsync_neg, vsync_neg, 43.269, 85.008}, + + { 800, 600, 56, 36.000, { 800, 24, 72, 128, 600, 1, 2, 22}, + hsync_pos, vsync_pos, 35.156, 56.250}, + { 800, 600, 60, 40.000, { 800, 40, 128, 88, 600, 1, 4, 23}, + hsync_pos, vsync_pos, 37.879, 60.317}, + { 800, 600, 72, 50.000, { 800, 56, 120, 64, 600,37, 6, 23}, + hsync_pos, vsync_pos, 48.077, 72.188}, + { 800, 600, 75, 49.500, { 800, 16, 80, 160, 600, 1, 3, 21}, + hsync_pos, vsync_pos, 46.875, 75.000}, + { 800, 600, 85, 56.250, { 800, 32, 64, 152, 600, 1, 3, 27}, + hsync_pos, vsync_pos, 53.674, 85.061}, + + {1024, 768, 43, 44.900, {1024, 8, 176, 56, 768, 0, 4, 20}, + hsync_pos, vsync_pos, 35.522, 86.957}, + {1024, 768, 60, 65.000, {1024, 24, 136, 160, 768, 3, 6, 29}, + hsync_neg, vsync_neg, 48.363, 60.004}, + {1024, 768, 70, 75.000, {1024, 24, 136, 144, 768, 3, 6, 29}, + hsync_neg, vsync_neg, 56.476, 70.069}, + {1024, 768, 75, 78.750, {1024, 16, 96, 176, 768, 1, 3, 28}, + hsync_pos, vsync_pos, 60.023, 75.029}, + {1024, 768, 85, 94.500, {1024, 48, 96, 208, 768, 1, 3, 36}, + hsync_pos, vsync_pos, 68.677, 84.997}, + + {1152, 864, 70, 94.200, {1152, 32, 96, 192, 864, 1, 3, 46}, + hsync_pos, vsync_pos, 0.000, 0.000}, + {1152, 864, 75, 108.000, {1152, 64, 128, 256, 864, 1, 3, 32}, + hsync_pos, vsync_pos, 67.500, 75.000}, + {1152, 864, 85, 121.500, {1152, 64, 128, 224, 864, 1, 3, 43}, + hsync_pos, vsync_pos, 0.000, 0.000}, + + {1280, 960, 60, 108.000, {1280, 96, 112, 312, 960, 1, 3, 36}, + hsync_pos, vsync_pos, 60.000, 60.000}, + {1280, 960, 85, 148.500, {1280, 64, 160, 224, 960, 1, 3, 47}, + hsync_pos, vsync_pos, 85.398, 85.002}, + + {1280, 1024, 60, 108.000, {1280, 48, 112, 248, 1024, 1, 3, 38}, + hsync_pos, vsync_pos, 63.981, 60.020}, + {1280, 1024, 75, 135.000, {1280, 16, 144, 248, 1024, 1, 3, 38}, + hsync_pos, vsync_pos, 79.976, 75.025}, + {1280, 1024, 85, 157.500, {1280, 64, 160, 224, 1024, 1, 3, 44}, + hsync_pos, vsync_pos, 91.146, 85.024}, + + {1600, 1200, 60, 162.000, {1600, 64, 192, 304, 1200, 1, 3, 46}, + hsync_pos, vsync_pos, 75.000, 60.000}, + {1600, 1200, 65, 175.500, {1600, 64, 192, 304, 1200, 1, 3, 46}, + hsync_pos, vsync_pos, 81.250, 65.000}, + {1600, 1200, 70, 189.000, {1600, 64, 192, 304, 1200, 1, 3, 46}, + hsync_pos, vsync_pos, 87.500, 70.000}, + {1600, 1200, 75, 202.500, {1600, 64, 192, 304, 1200, 1, 3, 46}, + hsync_pos, vsync_pos, 93.750, 75.000}, + {1600, 1200, 85, 229.500, {1600, 64, 192, 304, 1200, 1, 3, 46}, + hsync_pos, vsync_pos, 106.250, 85.000}, + + {1792, 1344, 60, 204.750, {1792,128, 200, 328, 1344, 1, 3, 46}, + hsync_neg, vsync_pos, 83.640, 60.000}, + {1792, 1344, 75, 261.000, {1792, 96, 216, 352, 1344, 1, 3, 69}, + hsync_neg, vsync_pos, 106.270, 74.997}, + + {1856, 1392, 60, 218.250, {1856, 96, 224, 352, 1392, 1, 3, 43}, + hsync_neg, vsync_pos, 86.333, 59.995}, + {1856, 1392, 75, 288.000, {1856,128, 224, 352, 1392, 1, 3,104}, + hsync_neg, vsync_pos, 112.500, 75.000}, + + {1920, 1440, 60, 234.000, {1920,128, 208, 344, 1440, 1, 3, 56}, + hsync_neg, vsync_pos, 90.000, 60.000}, + {1920, 1440, 75, 297.000, {1920,144, 224, 352, 1440, 1, 3, 56}, + hsync_neg, vsync_pos, 112.500, 75.000}, + + { 0, 0, 0, 0.000, { 0, 0, 0, 0, 0, 0, 0, 0}, + 000000000, 000000000, 0.000, 0.000}, +}; diff --git a/tools/ddcprobe/vesamode.h b/tools/ddcprobe/vesamode.h new file mode 100644 index 000000000..b7eef9283 --- /dev/null +++ b/tools/ddcprobe/vesamode.h @@ -0,0 +1,31 @@ +#ifndef vesamode_h +#define vesamode_h +#include <sys/types.h> +#ident "$Id$" + +typedef enum { hsync_neg = 0, hsync_pos } hsync_t; +typedef enum { vsync_neg = 0, vsync_pos } vsync_t; + +struct vesa_mode_t { + u_int16_t number; + u_int16_t x, y; + u_int32_t colors; + const char *text; + const char *modeline; +}; + +struct vesa_timing_t { + u_int16_t x, y; + float refresh; + float dotclock; + u_int16_t timings[8]; + hsync_t hsync; + vsync_t vsync; + float hfreq; + float vfreq; +}; + +extern struct vesa_mode_t known_vesa_modes[]; +extern struct vesa_timing_t known_vesa_timings[]; + +#endif /* vesamode_h */ |