/* Copyright (C) 2011 Andy Getzendanner // // Hereby licensed AS-IS to anyone who wants it // // Responsibility and liability for unintended consequences including // // hardware damage are hereby disclaimed; USE ONLY AT YOUR OWN RISK // // Note: compile with at least -O due to inlined SMBus functions // // Note: errors in i2c-dev.h likely result from using the kernel-internal // // version of that file rather than the userspace-facing one from the // // i2c-tools package, available from: // // http://www.lm-sensors.org/wiki/I2CTools */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define EDID_I2C_ADDR 0x50 #define EDID_BLKSIZE 128 #define iferror(COND, FUNC) _iferror(COND, FUNC, __LINE__) #define ifwarn(COND, FUNC) _ifwarn(COND, FUNC, __LINE__) #define sizeel(X) sizeof(*X) #define numel(X) (sizeof(X)/sizeel(X)) const uint8_t EDID_HEADER[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; int main(int argc, char *argv[], char *envp[]); void usage(void) __attribute__ ((__noreturn__)); void _iferror(int cond, const char *func, int line); void _ifwarn(int cond, const char *func, int line); void fhexbuf(FILE *stream, const uint8_t *buf, size_t size); uint8_t checksum(const uint8_t *buf, size_t size); void checkedid(const uint8_t *edid, int fatal); int main(int argc, char *argv[], char *envp[]) { int quiet = 0; int force = 0; argv++; argc--; if (argc < 1) usage(); if (!strcmp(argv[0], "-q")) { quiet = 1; argv++; argc--; if (argc < 1) usage(); } if (!strcmp(argv[0], "-f")) { force = 1; argv++; argc--; if (argc < 1) usage(); } int fd = open(argv[0], O_RDWR); iferror(fd < 0, "open"); argv++; argc--; int ret = ioctl(fd, I2C_SLAVE, (ptrdiff_t) EDID_I2C_ADDR); iferror(ret == -1, "ioctl"); if (argc != 1) usage(); if (!strcmp(argv[0], "read")) { uint8_t buf[EDID_BLKSIZE]; size_t addr = 0x00; for (size_t addr = 0; addr < sizeof(buf); addr++) { int32_t rb = i2c_smbus_read_byte_data(fd, (uint8_t) addr); iferror(rb == -1, "i2c_smbus_read_byte_data"); buf[addr] = (uint8_t) rb; } if (!quiet) fhexbuf(stderr, buf, sizeof(buf)); checkedid(buf, 0); size_t wbc = fwrite(buf, sizeel(buf), numel(buf), stdout); iferror(ferror(stdout), "fwrite"); iferror(wbc < numel(buf), "fwrite"); return EXIT_SUCCESS; } else if (!strcmp(argv[0], "write")) { uint8_t buf[EDID_BLKSIZE]; size_t rbc = fread(buf, sizeel(buf), numel(buf), stdin); iferror(ferror(stdin), "fread"); iferror(rbc < numel(buf), "fread"); if (!quiet) fhexbuf(stderr, buf, sizeof(buf)); checkedid(buf, !force); for (size_t addr = 0; addr < sizeof(buf); addr++) { int32_t smret = i2c_smbus_write_byte_data(fd, (uint8_t) addr, buf[addr]); iferror(smret == -1, "i2c_smbus_write_byte_data"); } return EXIT_SUCCESS; } else if (!strcmp(argv[0], "check")) { uint8_t buf[EDID_BLKSIZE]; size_t rbc = fread(buf, sizeel(buf), numel(buf), stdin); iferror(ferror(stdin), "fread"); iferror(rbc < numel(buf), "fread"); if (!quiet) fhexbuf(stderr, buf, sizeof(buf)); checkedid(buf, 1); return EXIT_SUCCESS; } else if (!strcmp(argv[0], "fix")) { uint8_t buf[EDID_BLKSIZE]; size_t rbc = fread(buf, sizeel(buf), numel(buf), stdin); iferror(ferror(stdin), "fread"); iferror(rbc < numel(buf), "fread"); if (!quiet) fhexbuf(stderr, buf, sizeof(buf)); int change = 0; if (memcmp(buf, EDID_HEADER, sizeof(EDID_HEADER))) { (void) fprintf(stderr, "INFO at %d: Fixing header\n", __LINE__); (void) memcpy(buf, EDID_HEADER, sizeof(EDID_HEADER)); change = 1; } uint8_t cksum = 0x00 - checksum(buf, EDID_BLKSIZE - 1); if (buf[EDID_BLKSIZE - 1] != cksum) { (void) fprintf(stderr, "INFO at %d: Fixing checksum\n", __LINE__); buf[EDID_BLKSIZE - 1] = cksum; change = 1; } if (change && !quiet) fhexbuf(stderr, buf, sizeof(buf)); size_t wbc = fwrite(buf, sizeel(buf), numel(buf), stdout); iferror(ferror(stdout), "fwrite"); iferror(wbc < numel(buf), "fwrite"); return EXIT_SUCCESS; } else usage(); } void usage(void) { (void) fputs("Usage: edid-tool [-q] [-f] i2cdev read|write|tryfix\n" " i2cdev - probably like /dev/i2c-%d\n" " read - outputs a binary blob to stdout\n" " write - reads a binary blob from stdin\n" " check - reads a binary blob from stdin and checks its header and\n" " checksum\n" " fix - reads a binary blob from stdin, corrects the header and\n" " checksum if necessary, and writes to stdout\n" " -q - suppresses output of hex block to stderr\n" " -f - ignores checksum and header errors when writing EDID checksum\n" " argument order must be as specified above\n", stderr); exit(EXIT_FAILURE); } void _iferror(int cond, const char *func, int line) { if (cond) { (void) fprintf(stderr, "ERROR at %d: %s() failed: %s\n", line, func, strerror(errno)); exit(EXIT_FAILURE); } else return; } void _ifwarn(int cond, const char *func, int line) { if (cond) (void) fprintf(stderr, "WARN at %d: %s() failed: %s\n", line, func, strerror(errno)); return; } void fhexbuf(FILE *stream, const uint8_t *buf, size_t size) { (void) fprintf(stream, " "); for (int addr = 0; addr < 0x10; addr++) (void) fprintf(stream, (addr % 0x04 == 0x03) ? "%2x " : "%2x ", addr); (void) fputc(' ', stream); for (int addr = 0; addr < 0x10; addr++) { (void) fprintf(stream, "%1x", addr); if ((addr % 0x04 == 0x03) && (addr % 0x10 != 0x0f)) (void) fputc(' ', stream); } (void) fputc(' ', stream); (void) fputc('\n', stream); for (size_t rowaddr = 0; rowaddr < size; rowaddr += 0x10) { (void) fprintf(stream, "%08zx ", rowaddr); for (size_t addr = rowaddr; (addr < rowaddr + 0x10) && (addr < size); addr++) (void) fprintf(stream, (addr % 0x04 == 0x03) ? "%02"PRIx8" " : "%02"PRIx8" ", buf[addr]); for (size_t addr = size; addr < rowaddr + 0x10; addr++) (void) fprintf(stream, (addr % 0x04 == 0x03) ? " " : " ", buf[addr]); (void) fputc('|', stream); for (size_t addr = rowaddr; (addr < rowaddr + 0x10) && (addr < size); addr++) { (void) fputc(isprint(buf[addr]) ? buf[addr] : '.', stream); if ((addr % 0x04 == 0x03) && (addr % 0x10 != 0x0f) && (addr < size - 1)) (void) fputc(' ', stream); } (void) fputc('|', stream); (void) fputc('\n', stream); } return; } inline uint8_t checksum(const uint8_t *buf, size_t size) { uint8_t sum = 0x00; while (size--) sum += buf[size]; return sum; } void checkedid(const uint8_t *edid, int fatal) { int bad = 0; if (memcmp(edid, EDID_HEADER, sizeof(EDID_HEADER))) { (void) fprintf(stderr, "%s at %d: Bad header: 0x" "%02"PRIx8"%02"PRIx8" %02"PRIx8"%02"PRIx8" " "%02"PRIx8"%02"PRIx8" %02"PRIx8"%02"PRIx8"\n", fatal ? "ERROR" : "WARN", __LINE__, edid[0], edid[1], edid[2], edid[3], edid[4], edid[5], edid[6], edid[7]); bad = 1; } uint8_t cksum = checksum(edid, EDID_BLKSIZE); if (cksum) { (void) fprintf(stderr, "%s at %d: Bad checksum: 0x%02"PRIx8"\n", fatal ? "ERROR" : "WARN", __LINE__, cksum); bad = 1; } if (fatal && bad) exit(EXIT_FAILURE); else return; }