#include #include #include #include #define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a)) typedef unsigned char u8; typedef unsigned short int u16; typedef unsigned long int u32; static u16 port; static void error(const char *msg) { fprintf(stderr, "error: %s\n", msg); exit(1); } /* * The following code was written for Borland Turbo C 2.0. * * When porting to another compiler, the direct register accesses and * __int__() could be converted to inline assembler. */ /* * Makes sure that the PCI BIOS functions are available. */ static void check_pci_bios(void) { _AX = 0xb101; _DX = 0; __int__(0x1a); if (_DX != 0x4350) error("PCI BIOS not found; do not run this in a DOS box"); } /* * Searches for a PCI device with the specified ID. * Returns its address as a 16-bit value (8 bits bus, 5 bits device, * 3 bits function); or -1 if not found. */ static u16 find_pci_device(u16 vendor, u16 device) { _AX = 0xb102; _CX = device; _DX = vendor; _SI = 0; /* let's hope nobody has more than one of these cards ... */ __int__(0x1a); if (_AH == 0) return _BX; else return (u16)-1; } /* * Reads a 16-bit register from a PCI device's configuration space. * 'addr' is bus/device/fn number as returned by find_pci_device(). */ static u16 read_pci_cfg16(u16 addr, u16 reg) { _AX = 0xb109; _BX = addr; _DI = reg; __int__(0x1a); return _CX; } /* * Reads a 32-bit register from a PCI device's configuration space. */ static u32 read_pci_cfg32(u16 addr, u16 reg) { u16 l, h; _AX = 0xb10a; _BX = addr; _DI = reg; __int__(0x1a); /* return _ECX; */ l = _CX; __emit__(0x66, 0xc1, 0xe9, 0x10); /* shr ecx,16 */ h = _CX; return ((u32)h << 16) | l; } static void wait_ms(u16 milliseconds) { u32 microseconds = milliseconds * 1000L; _CX = (u16)(microseconds >> 16); _DX = (u16)microseconds; _AH = 0x86; __int__(0x15); } /* * Waits for up to 5 ms or for the EEPROM being not busy. */ static void wait_for_eeprom(void) { unsigned int wait; wait = 0; for (;;) { if (!(inportb(port + 0x81) & 0x80)) return; ++wait; if (wait >= 5) error("EEPROM timeout\n" "It looks as if your EEPROM chip is broken."); wait_ms(1); } } static u16 read_eeprom(u8 index) { wait_for_eeprom(); outportb(port + 0x80, index); wait_for_eeprom(); return inport(port + 0x82); } static void write_eeprom(u8 index, u16 data) { wait_for_eeprom(); outport(port + 0x82, data); outportb(port + 0x80, index | 0x80); wait_for_eeprom(); } /* * Asks for confirmation, and aborts if the user did not enter "yes". */ static void ask_confirmation(void) { char buf[256]; fputs("==> ", stdout); fflush(stdout); if (!fgets(buf, sizeof buf, stdin) || (strcmp(buf, "yes\n") && /* Y/Z keys are exchanged on some non-US keyboards */ strcmp(buf, "zes\n"))) { puts("\nExiting ..."); exit(0); } putchar('\n'); } /* * List of known sub-device/-vendor IDs. */ static struct { u16 vendor; u16 device; const char *name; } ids[] = { { 0x1043, 0x8269, "Asus Xonar D2" }, { 0x1043, 0x8275, "Asus Xonar DX" }, { 0x1043, 0x82b7, "Asus Xonar D2X" }, { 0x1043, 0x8314, "Asus Xonar HDAV1.3" }, { 0x1043, 0x8327, "Asus Xonar DX" }, { 0x1043, 0x834f, "Asus Xonar D1" }, { 0x1043, 0x835c, "Asus Xonar Essence STX" }, { 0x1043, 0x835d, "Asus Xonar Essence ST" }, { 0x1043, 0x835e, "Asus Xonar HDAV1.3 Slim" }, { 0x1043, 0x838e, "Asus Xonar DS" }, { 0x1043, 0x8428, "Asus Xonar Xense" }, { 0x1043, 0x8467, "Asus Xonar DG" }, { 0x1043, 0x8521, "Asus Xonar DGX" }, { 0x1043, 0x8522, "Asus Xonar DSX" }, { 0x10b0, 0x0216, "(unknown)" }, { 0x10b0, 0x0217, "(unknown)" }, { 0x10b0, 0x0218, "(unknown)" }, { 0x10b0, 0x0219, "(unknown)" }, { 0x13f6, 0x0001, "(unknown)" }, { 0x13f6, 0x0010, "(unknown)" }, { 0x13f6, 0x8782, "(unknown) PCI 2.0 HD Audio" }, { 0x13f6, 0x8788, "generic card - C-Media reference design" }, { 0x13f6, 0xffff, "Kuroutoshikou CMI8787-HG2PCI" }, { 0x147a, 0xa017, "(unknown)" }, { 0x14c3, 0x1710, "TempoTec HiFier Fantasia" }, { 0x14c3, 0x1711, "TempoTec HiFier Serenade" }, { 0x1a58, 0x0910, "Razer Barracuda AC-1" }, { 0x415a, 0x5431, "AuzenTech X-Meridian" }, { 0x5431, 0x017a, "AuzenTech X-Meridian 2G" }, { 0x7284, 0x9761, "HT-Omega Claro" }, { 0x7284, 0x9781, "HT-Omega Claro halo" }, { 0x7284, 0x9783, "HT-Omega eClaro" }, }; /* * Returns index of match in ids[], or -1 if not found. */ static int search_id(u16 device) { int i; /* * Do not check the vendor ID because the product IDs are unique enough * and the vendor ID might be garbled. */ for (i = 0; i < ARRAY_SIZE(ids); ++i) if (device == ids[i].device) return i; return -1; } /* * Asks user for a card model; returns index in ids[]. */ static int ask_card(void) { char buf[256]; char *endptr; unsigned int num; printf("Enter a number between 1 and %d to write that subsystem ID into the EEPROM.\n", (int)ARRAY_SIZE(ids)); puts("Enter anything else, or nothing, to abort."); for (num = 0; num < ARRAY_SIZE(ids); ++num) { if (strstr(ids[num].name, "(unknown")) continue; printf("%2u: %04x:%04x (%s)\n", num + 1, ids[num].vendor, ids[num].device, ids[num].name); } fputs("==> ", stdout); fflush(stdout); if (!fgets(buf, sizeof buf, stdin)) { do_exit: puts("\nExiting ..."); exit(0); } num = (unsigned int)strtol(buf, &endptr, 10); if (*endptr != '\n') goto do_exit; if (num < 1 || num > ARRAY_SIZE(ids)) goto do_exit; putchar('\n'); return num - 1; } static void wait_enter(void) { char buf[256]; fgets(buf, sizeof buf, stdin); } int main() { u16 addr; u32 portl; u16 eeprom_id, eeprom_vendor, eeprom_device; int eeprom_valid; int card_index; int ok; check_pci_bios(); puts("\nsearching for sound card with CMI8788 chip ..."); addr = find_pci_device(0x13f6, 0x8788); if (addr == (u16)-1) error("no PCI card with CMI8788 chip found"); printf("found on PCI bus %u, device %u\n\n", addr >> 8, (addr >> 3) & 0x1f); puts("determining port address ..."); portl = read_pci_cfg32(addr, 0x10); if (!(portl & 0x00000001)) error("I/O port range not found"); if (!(read_pci_cfg16(addr, 0x04) & 0x0001)) error("I/O ports are not enabled"); if (portl > 0xff00) error("invalid I/O port address"); port = (u16)portl & ~3; printf("port address is %04x\n\n", port); /* make sure EEPROM pins are not used for SPI */ outportb(port + 0x50, inportb(port + 0x50) & ~0x80); puts("reading EEPROM ..."); eeprom_id = read_eeprom(0); eeprom_valid = eeprom_id == 0x434d; printf("EEPROM ID is %04x - %s\n", eeprom_id, eeprom_valid ? "valid" : "invalid; this means that the subsystem ID is ignored"); eeprom_vendor = read_eeprom(1); eeprom_device = read_eeprom(2); if (eeprom_device != 0xffff || eeprom_vendor == 0x13f6) card_index = search_id(eeprom_device); else card_index = -1; printf("subsystem ID is %04x:%04x (%s)\n\n", eeprom_vendor, eeprom_device, card_index >= 0 ? ids[card_index].name : "UNKNOWN"); /* * Below, we check that the device ID is one of the currently known * device IDs. From the reports, it looks as if only the EEPROM * word at index 0 (the EEPROM ID) was overwritten, so we might just * write the correct ID into word 0 and not bother about the vendor/ * device IDs. */ if (eeprom_valid && eeprom_vendor != 0 && eeprom_vendor != 0xffff && eeprom_device != 0 && (eeprom_device != 0xffff || eeprom_vendor == 0x13f6)) { puts("The EEPROM seems to be OK."); puts("Enter 'yes' if you really want to change the subsystem ID."); puts("Enter anything else to abort (recommended)."); ask_confirmation(); card_index = ask_card(); } else { if (card_index < 0) { card_index = ask_card(); } else if (eeprom_vendor != ids[card_index].vendor) { puts("The EEPROM ID and subsystem vendor ID are wrong."); printf("Enter 'yes' to write the correct IDs for \"%s\" into the EEPROM.\n", ids[card_index].name); puts("Enter anything else to abort."); ask_confirmation(); } else { puts("The EEPROM ID is wrong."); puts("Enter 'yes' to write the correct ID into the EEPROM."); puts("Enter anything else to abort."); ask_confirmation(); } } if (eeprom_valid && ids[card_index].vendor == eeprom_vendor && ids[card_index].device == eeprom_device) puts("nothing to be done :-)"); if (!eeprom_valid) { puts("writing EEPROM ID: 434d ..."); write_eeprom(0, 0x434d); /* "CM" */ } if (ids[card_index].vendor != eeprom_vendor) { printf("writing subsystem vendor ID: %04x ...\n", ids[card_index].vendor); write_eeprom(1, ids[card_index].vendor); } if (ids[card_index].device != eeprom_device) { printf("writing subsystem device ID: %04x ...\n", ids[card_index].device); write_eeprom(2, ids[card_index].device); } ok = 1; if (!eeprom_valid) { puts("checking EEPROM ID ..."); eeprom_id = read_eeprom(0); if (eeprom_id != 0x434d) { ok = 0; puts("FAILED"); } else { puts("OK"); } } if (ids[card_index].vendor != eeprom_vendor) { puts("checking subsystem vendor ID ..."); eeprom_vendor = read_eeprom(1); if (eeprom_vendor != ids[card_index].vendor) { ok = 0; puts("FAILED"); } else { puts ("OK"); } } if (ids[card_index].device != eeprom_device) { puts("checking subsystem device ID ..."); eeprom_device = read_eeprom(2); if (eeprom_device != ids[card_index].device) { ok = 0; puts("FAILED"); } else { puts ("OK"); } } if (ok) { puts("\nSuccess.\n\n" "Press Enter to reboot."); wait_enter(); ((void (far*)(void))MK_FP(0xffff, 0))(); } else { puts("\nWriting to the EEPROM failed. Maybe try again.\n"); } return 0; }