#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 0 #else #define cpuemu 1 #endif #include int box_is_xbox(); /* also used by libint10 */ void log_err(char *format, ...) { if (verbose) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } } /* * 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 *start, *end; for (start = s; start < s + len; start++) if (start[0] > ' ') break; for (end = &s[len - 1]; end >= start; end--) { if (end[0] > ' ') break; end[0] = 0; } return start; } 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; } static char *get_str(unsigned char *buf, unsigned buf_size, char *v) { int len = get_data(buf, buf_size, segofs2addr(v)); return canon_str(buf, len); } #define GET_WORD(ADDR, OFS) ((ADDR)[OFS] + ((ADDR)[(OFS) + 1] << 8)) static 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) { log_err("VBE: Error (0x4f00): 0x%04x\n", i); return 0; } { unsigned char tmp[1024]; /* Parse VBE block */ int version = GET_WORD(v, 0x04); int oem_version = GET_WORD(v, 0x14); int memory_size = GET_WORD(v, 0x12) << 16; log_err("VBE version: %u.%u, oem version = %u.%u\n", version >> 8, version & 0xff, oem_version >> 8, oem_version & 0xff); log_err("Memory: %uk\n", memory_size >> 10); log_err("OEM name: %s\n", get_str(tmp, sizeof tmp, v + 0x06)); log_err("Vendor name: %s\n", get_str(tmp, sizeof tmp, v + 0x16)); log_err("Product name: %s\n", get_str(tmp, sizeof tmp, v + 0x1a)); log_err("Product revision: %s\n", get_str(tmp, sizeof tmp, v + 0x1e)); } 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) { log_err("EDID: Error (0x4f15): 0x%04x\n", i); return 0; } return 1; } int get_edid(char *edid) { int pci_config_type = 1; /* Determine PCI configuration type */ int ok; /* Initialize Int10 */ ok = box_is_xbox() || InitInt10(pci_config_type) == 0; if (ok) { ok = (box_is_xbox() || vbe_check_vbe_info()) && vbe_get_edid_info(edid); FreeInt10(); } else if (getuid() != 0) fprintf(stderr, "you must be root to run this program\n"); return ok ? 128 : 0; } int box_is_xbox() { int is_xbox = 0; int result = -1; int fd; size_t rd; char *xbox_id = "0000\t10de02a5"; char id[13]; if (!(fd = open("/proc/bus/pci/devices", O_RDONLY))) { printf("Unable to open /proc/bus/pci/devices\n"); } if (!(rd = read(fd, id, sizeof(id)))) { printf("Unable to read /proc/bus/pci/devices\n"); } if (fd > 0) close(fd); result = strncmp(id, xbox_id, 13); if (result == 0) is_xbox = 1; return is_xbox; }