#include #if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) #include #endif #include #include #include #include #include #include #include #include #include #include #include "get-edid.h" #include "int10/vbios.h" #ifdef __i386__ #define cpuemu 1 #else #define cpuemu 0 #endif #include /* used by libint10 */ void log_err(char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } #ifdef DEBUG /* Record returned by int 0x10, function 0x4f, subfunction 0x00. */ struct vbe_info { unsigned int version; unsigned int oem_version; unsigned int memory_size; char *oem_name; char *vendor_name; char *product_name; char *product_revision; unsigned int modes; unsigned int mode_list[0x100]; }; /* * Create a 'canonical' version, i.e. no spaces at start and end. * * Note: removes chars >= 0x80 as well (due to (char *))! This * is currently considered a feature. */ static char *canon_str(char *s, int len) { char *m2, *m1, *m0 = malloc(len + 1); int i; for(m1 = m0, i = 0; i < len; i++) { if(m1 == m0 && s[i] <= ' ') continue; *m1++ = s[i]; } *m1 = 0; while(m1 > m0 && m1[-1] <= ' ') { *--m1 = 0; } m2 = strdup(m0); free(m0); return m2; } static unsigned segofs2addr(unsigned char *segofs) { return segofs[0] + (segofs[1] << 8) + (segofs[2] << 4)+ (segofs[3] << 12); } static unsigned get_data(unsigned char *buf, unsigned buf_size, unsigned addr) { unsigned bufferaddr = 0x7e00; unsigned len; *buf = 0; len = 0; if(addr >= bufferaddr && addr < bufferaddr + 0x200) { len = bufferaddr + 0x200 - addr; if(len >= buf_size) len = buf_size - 1; memcpy(buf, addr + (char *) 0, len); } else if(addr >= 0x0c0000 && addr < 0x100000) { len = 0x100000 - addr; if(len >= buf_size) len = buf_size - 1; memcpy(buf, addr + (char *) 0, len); } buf[len] = 0; return len; } #endif #define GET_WORD(ADDR, OFS) ((ADDR)[OFS] + ((ADDR)[(OFS) + 1] << 8)) int vbe_check_vbe_info(void) { int i; unsigned char v[0x200]; int ax, bx, cx; /* Setup registers for the interrupt call */ ax = 0x4f00; bx = 0; cx = 0; memset(v, 0, sizeof(v)); strcpy(v, "VBE2"); /* Get VBE block */ i = CallInt10(&ax, &bx, &cx, v, sizeof(v), cpuemu) & 0xffff; if (i != 0x4f) { #ifdef DEBUG log_err("VBE: Error (0x4f00): 0x%04x\n", i); #endif return 0; } #ifdef DEBUG { int l, u; unsigned char tmp[1024]; struct vbe_info vbe; /* Parse VBE block */ vbe.version = GET_WORD(v, 0x04); vbe.oem_version = GET_WORD(v, 0x14); vbe.memory_size = GET_WORD(v, 0x12) << 16; log_err("version = %u.%u, oem version = %u.%u\n", vbe.version >> 8, vbe.version & 0xff, vbe.oem_version >> 8, vbe.oem_version & 0xff); log_err("memory = %uk\n", vbe.memory_size >> 10); l = get_data(tmp, sizeof tmp, u = segofs2addr(v + 0x06)); vbe.oem_name = canon_str(tmp, l); log_err("oem name [0x%05x] = \"%s\"\n", u, vbe.oem_name); l = get_data(tmp, sizeof tmp, u = segofs2addr(v + 0x16)); vbe.vendor_name = canon_str(tmp, l); log_err("vendor name [0x%05x] = \"%s\"\n", u, vbe.vendor_name); l = get_data(tmp, sizeof tmp, u = segofs2addr(v + 0x1a)); vbe.product_name = canon_str(tmp, l); log_err("product name [0x%05x] = \"%s\"\n", u, vbe.product_name); l = get_data(tmp, sizeof tmp, u = segofs2addr(v + 0x1e)); vbe.product_revision = canon_str(tmp, l); log_err("product revision [0x%05x] = \"%s\"\n", u, vbe.product_revision); l = get_data(tmp, sizeof tmp, u = segofs2addr(v + 0x0e)) >> 1; for(i = vbe.modes = 0; i < l && i < sizeof vbe.mode_list / sizeof *vbe.mode_list; i++) { u = GET_WORD(tmp, 2 * i); if(u != 0xffff) vbe.mode_list[vbe.modes++] = u; else break; } log_err("%u video modes\n", vbe.modes); } #endif return 1; } /* Get EDID info. */ int vbe_get_edid_info(char *edid) { int i; int ax, bx, cx; /* Setup registers for the interrupt call */ ax = 0x4f15; bx = 1; cx = 0; /* Get EDID block */ i = CallInt10(&ax, &bx, &cx, edid, 256, cpuemu) & 0xffff; if (i != 0x4f) { #ifdef DEBUG log_err("EDID: Error (0x4f15): 0x%04x\n", i); #endif return 0; } return 1; } int get_edid(char *edid) { int pci_config_type = 1; /* Determine PCI configuration type */ int ok; /* Initialize Int10 */ ok = InitInt10(pci_config_type) == 0; if (ok) { ok = vbe_check_vbe_info() && vbe_get_edid_info(edid); FreeInt10(); } return ok ? 128 : 0; }