summaryrefslogtreecommitdiffstats
path: root/tools/ddcprobe
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ddcprobe')
-rw-r--r--tools/ddcprobe/.cvsignore1
-rw-r--r--tools/ddcprobe/Makefile18
-rw-r--r--tools/ddcprobe/ddcxinfos.c66
-rw-r--r--tools/ddcprobe/lrmi.c911
-rw-r--r--tools/ddcprobe/lrmi.h85
-rw-r--r--tools/ddcprobe/vbe.c709
-rw-r--r--tools/ddcprobe/vbe.h312
-rw-r--r--tools/ddcprobe/vesamode.c132
-rw-r--r--tools/ddcprobe/vesamode.h31
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(&regs, 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, &regs) == 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(&regs, 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, &regs) == 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(&regs, 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, &regs) == 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(&regs, 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, &regs) == 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(&regs, 0, sizeof(regs));
+ regs.eax = 0x4f03;
+
+ /* Do it. */
+ iopl(3);
+ ioperm(0, 0x400, 1);
+
+ if(LRMI_int(0x10, &regs) == 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(&regs, 0, sizeof(regs));
+ regs.eax = 0x4f02;
+ regs.ebx = mode;
+
+ /* Do it. */
+ iopl(3);
+ ioperm(0, 0x400, 1);
+ LRMI_int(0x10, &regs);
+
+ /* 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(&regs, 0, sizeof(regs));
+ regs.eax = 0x4f04;
+ regs.ecx = 0xffff;
+ regs.edx = 0;
+
+ iopl(3);
+ ioperm(0, 0x400, 1);
+
+ if(LRMI_int(0x10, &regs) == 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(&regs, 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, &regs) == 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(&regs, 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, &regs) == 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(&regs, 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, &regs) == 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 */