/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Implementation of ISA Plug and Play. * * * * Authors: Miran Grca, * RichardG, * * Copyright 2016-2018 Miran Grca. * Copyright 2021 RichardG. */ #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> #include <86box/io.h> #include <86box/isapnp.h> #include <86box/plat_unused.h> #define CHECK_CURRENT_LD() \ if (!ld) { \ isapnp_log("ISAPnP: No logical device selected\n"); \ goto vendor_defined; \ } #define CHECK_CURRENT_CARD() \ if (!card) { \ isapnp_log("ISAPnP: No card in CONFIG state\n"); \ break; \ } const uint8_t isapnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE, 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 }; static const device_t isapnp_device; #ifdef ENABLE_ISAPNP_LOG int isapnp_do_log = ENABLE_ISAPNP_LOG; static void isapnp_log(const char *fmt, ...) { va_list ap; if (isapnp_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define isapnp_log(fmt, ...) #endif enum { PNP_STATE_WAIT_FOR_KEY = 0, PNP_STATE_CONFIG, PNP_STATE_ISOLATION, PNP_STATE_SLEEP }; typedef struct _isapnp_device_ { uint8_t number; uint8_t regs[256]; uint8_t mem_upperlimit; uint8_t irq_types; uint8_t io_16bit; uint8_t io_len[8]; const isapnp_device_config_t *defaults; struct _isapnp_device_ *next; } isapnp_device_t; typedef struct _isapnp_card_ { uint8_t enable; uint8_t state; uint8_t csn; uint8_t ld; uint8_t id_checksum; uint8_t serial_read; uint8_t serial_read_pair; uint8_t serial_read_pos; uint8_t *rom; uint16_t rom_pos; uint16_t rom_size; void *priv; /* ISAPnP memory and I/O addresses are awkwardly big endian, so we populate this structure whenever something on some device changes, and pass it on instead. */ isapnp_device_config_t config; void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv); void (*csn_changed)(uint8_t csn, void *priv); uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv); void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv); isapnp_device_t *first_ld; struct _isapnp_card_ *next; } isapnp_card_t; typedef struct { uint8_t reg; uint8_t key_pos : 5; uint16_t read_data_addr; isapnp_card_t *first_card; isapnp_card_t *isolated_card; isapnp_card_t *current_ld_card; isapnp_device_t *current_ld; } isapnp_t; static void isapnp_device_config_changed(isapnp_card_t *card, isapnp_device_t *ld) { /* Ignore card if it hasn't signed up for configuration changes. */ if ((card == NULL) || !card->config_changed) return; /* Populate config structure, performing endianness conversion as needed. */ card->config.activate = ld->regs[0x30] & 0x01; uint8_t reg_base; for (uint8_t i = 0; i < 4; i++) { reg_base = 0x40 + (8 * i); card->config.mem[i].base = (ld->regs[reg_base] << 16) | (ld->regs[reg_base + 1] << 8); card->config.mem[i].size = (ld->regs[reg_base + 3] << 16) | (ld->regs[reg_base + 4] << 8); if (ld->regs[reg_base + 2] & 0x01) /* upper limit */ card->config.mem[i].size -= card->config.mem[i].base; } for (uint8_t i = 0; i < 4; i++) { reg_base = (i == 0) ? 0x76 : (0x80 + (16 * i)); card->config.mem32[i].base = (ld->regs[reg_base] << 24) | (ld->regs[reg_base + 1] << 16) | (ld->regs[reg_base + 2] << 8) | ld->regs[reg_base + 3]; card->config.mem32[i].size = (ld->regs[reg_base + 5] << 24) | (ld->regs[reg_base + 6] << 16) | (ld->regs[reg_base + 7] << 8) | ld->regs[reg_base + 8]; if (ld->regs[reg_base + 4] & 0x01) /* upper limit */ card->config.mem32[i].size -= card->config.mem32[i].base; } for (uint8_t i = 0; i < 8; i++) { reg_base = 0x60 + (2 * i); if (ld->regs[0x31] & 0x02) card->config.io[i].base = 0; /* let us handle I/O range check reads */ else card->config.io[i].base = (ld->regs[reg_base] << 8) | ld->regs[reg_base + 1]; } for (uint8_t i = 0; i < 2; i++) { reg_base = 0x70 + (2 * i); card->config.irq[i].irq = ld->regs[reg_base]; card->config.irq[i].level = ld->regs[reg_base + 1] & 0x02; card->config.irq[i].type = ld->regs[reg_base + 1] & 0x01; } for (uint8_t i = 0; i < 2; i++) { reg_base = 0x74 + i; card->config.dma[i].dma = ld->regs[reg_base]; } /* Signal the configuration change. */ card->config_changed(ld->number, &card->config, card->priv); } static void isapnp_reset_ld_config(isapnp_device_t *ld) { /* Do nothing if there's no default configuration for this device. */ const isapnp_device_config_t *config = ld->defaults; if (!config) return; /* Populate configuration registers. */ ld->regs[0x30] = !!config->activate; uint8_t reg_base; uint32_t size; for (uint8_t i = 0; i < 4; i++) { reg_base = 0x40 + (8 * i); ld->regs[reg_base] = config->mem[i].base >> 16; ld->regs[reg_base + 1] = config->mem[i].base >> 8; size = config->mem[i].size; if (ld->regs[reg_base + 2] & 0x01) /* upper limit */ size += config->mem[i].base; ld->regs[reg_base + 3] = size >> 16; ld->regs[reg_base + 4] = size >> 8; } for (uint8_t i = 0; i < 4; i++) { reg_base = (i == 0) ? 0x76 : (0x80 + (16 * i)); ld->regs[reg_base] = config->mem32[i].base >> 24; ld->regs[reg_base + 1] = config->mem32[i].base >> 16; ld->regs[reg_base + 2] = config->mem32[i].base >> 8; ld->regs[reg_base + 3] = config->mem32[i].base; size = config->mem32[i].size; if (ld->regs[reg_base + 4] & 0x01) /* upper limit */ size += config->mem32[i].base; ld->regs[reg_base + 5] = size >> 24; ld->regs[reg_base + 6] = size >> 16; ld->regs[reg_base + 7] = size >> 8; ld->regs[reg_base + 8] = size; } for (uint8_t i = 0; i < 8; i++) { reg_base = 0x60 + (2 * i); ld->regs[reg_base] = config->io[i].base >> 8; ld->regs[reg_base + 1] = config->io[i].base; } for (uint8_t i = 0; i < 2; i++) { reg_base = 0x70 + (2 * i); ld->regs[reg_base] = config->irq[i].irq; ld->regs[reg_base + 1] = (!!config->irq[i].level << 1) | !!config->irq[i].type; } for (uint8_t i = 0; i < 2; i++) { reg_base = 0x74 + i; ld->regs[reg_base] = config->dma[i].dma; } } static void isapnp_reset_ld_regs(isapnp_device_t *ld) { memset(ld->regs, 0, sizeof(ld->regs)); /* DMA disable uses a non-zero value. */ ld->regs[0x74] = ld->regs[0x75] = ISAPNP_DMA_DISABLED; /* Set the upper limit bit on memory ranges which require it. */ for (uint8_t i = 0; i < 4; i++) ld->regs[0x42 + (8 * i)] |= !!(ld->mem_upperlimit & (1 << i)); ld->regs[0x7a] |= !!(ld->mem_upperlimit & (1 << 4)); for (uint8_t i = 1; i < 4; i++) ld->regs[0x84 + (16 * i)] |= !!(ld->mem_upperlimit & (1 << (4 + i))); /* Set the default IRQ type bits. */ for (uint8_t i = 0; i < 2; i++) { if (ld->irq_types & (0x1 << (4 * i))) ld->regs[0x70 + (2 * i)] = 0x02; else if (ld->irq_types & (0x2 << (4 * i))) ld->regs[0x70 + (2 * i)] = 0x00; else if (ld->irq_types & (0x4 << (4 * i))) ld->regs[0x70 + (2 * i)] = 0x03; else if (ld->irq_types & (0x8 << (4 * i))) ld->regs[0x70 + (2 * i)] = 0x01; } /* Reset configuration registers to match the default configuration. */ isapnp_reset_ld_config(ld); } static uint8_t isapnp_read_rangecheck(UNUSED(uint16_t addr), void *priv) { const isapnp_device_t *dev = (isapnp_device_t *) priv; return (dev->regs[0x31] & 0x01) ? 0x55 : 0xaa; } static uint8_t isapnp_read_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg) { uint8_t ret = 0xff; uint8_t bit; uint8_t next_shift; switch (reg) { case 0x01: /* Serial Isolation */ card = dev->first_card; while (card) { if (card->enable && card->rom && (card->state == PNP_STATE_ISOLATION)) break; card = card->next; } dev->isolated_card = card; if (card) { if (card->serial_read_pair) { /* second byte (aa/00) */ card->serial_read <<= 1; if (!card->serial_read_pos) card->rom_pos = 0x09; } else { /* first byte (55/00) */ if (card->serial_read_pos < 64) { /* reading 64-bit vendor/serial */ bit = (card->rom[card->serial_read_pos >> 3] >> (card->serial_read_pos & 0x7)) & 0x01; next_shift = (!!(card->id_checksum & 0x02) ^ !!(card->id_checksum & 0x01) ^ bit) & 0x01; card->id_checksum >>= 1; card->id_checksum |= (next_shift << 7); } else { /* reading 8-bit checksum */ if (card->serial_read_pos == 64) /* populate ID checksum in ROM */ card->rom[0x08] = card->id_checksum; bit = (card->id_checksum >> (card->serial_read_pos & 0x7)) & 0x01; } isapnp_log("ISAPnP: Read bit %d of byte %02X (%02X) = %d\n", card->serial_read_pos & 0x7, card->serial_read_pos >> 3, card->rom[card->serial_read_pos >> 3], bit); card->serial_read = bit ? 0x55 : 0x00; card->serial_read_pos = (card->serial_read_pos + 1) % 72; } card->serial_read_pair ^= 1; ret = card->serial_read; } break; case 0x04: /* Resource Data */ CHECK_CURRENT_CARD(); isapnp_log("ISAPnP: Read resource data index %02X (%02X) from CSN %02X\n", card->rom_pos, card->rom[card->rom_pos], card->csn); if (card->rom_pos >= card->rom_size) ret = 0xff; else ret = card->rom[card->rom_pos++]; break; case 0x05: /* Status */ ret = 0x00; CHECK_CURRENT_CARD(); isapnp_log("ISAPnP: Query status for CSN %02X\n", card->csn); ret = 0x01; break; case 0x06: /* Card Select Number */ ret = 0x00; CHECK_CURRENT_CARD(); isapnp_log("ISAPnP: Query CSN %02X\n", card->csn); ret = card->csn; break; case 0x07: /* Logical Device Number */ ret = 0x00; CHECK_CURRENT_LD(); isapnp_log("ISAPnP: Query LDN for CSN %02X device %02X\n", card->csn, ld->number); ret = ld->number; break; case 0x20 ... 0x2f: case 0x38 ... 0x3f: case 0xa9 ... 0xff: vendor_defined: CHECK_CURRENT_CARD(); isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X device %02X\n", reg, card->csn, ld ? ld->number : -1); if (card->read_vendor_reg) ret = card->read_vendor_reg(ld ? ld->number : -1, reg, card->priv); break; default: if (reg >= 0x30) { CHECK_CURRENT_LD(); isapnp_log("ISAPnP: Read register %02X from CSN %02X device %02X\n", reg, card->csn, ld->number); ret = ld->regs[reg]; } break; } isapnp_log("ISAPnP: read_common(%02X) = %02X\n", reg, ret); return ret; } static uint8_t isapnp_read_data(UNUSED(uint16_t addr), void *priv) { isapnp_t *dev = (isapnp_t *) priv; isapnp_card_t *card = dev->first_card; while (card) { if (card->enable && (card->state == PNP_STATE_CONFIG)) break; card = card->next; } isapnp_log("ISAPnP: read_data() => "); return isapnp_read_common(dev, card, dev->current_ld, dev->reg); } static void isapnp_set_read_data(uint16_t addr, isapnp_t *dev) { /* Remove existing READ_DATA port if set. */ if (dev->read_data_addr) { io_removehandler(dev->read_data_addr, 1, isapnp_read_data, NULL, NULL, NULL, NULL, NULL, dev); dev->read_data_addr = 0; } /* Set new READ_DATA port if within range. */ if ((addr >= 0x203) && (addr <= 0x3ff)) { dev->read_data_addr = addr; io_sethandler(dev->read_data_addr, 1, isapnp_read_data, NULL, NULL, NULL, NULL, NULL, dev); } } static void isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv) { isapnp_t *dev = (isapnp_t *) priv; isapnp_card_t *card = dev->first_card; isapnp_log("ISAPnP: write_addr(%02X)\n", val); if (!card) /* don't do anything if we have no PnP cards */ return; dev->reg = val; if (card->state == PNP_STATE_WAIT_FOR_KEY) { /* checking only the first card should be fine */ /* Check written value against LFSR key. */ if (val == isapnp_init_key[dev->key_pos]) { dev->key_pos++; if (!dev->key_pos) { isapnp_log("ISAPnP: Key unlocked, putting cards to SLEEP\n"); while (card) { if (card->enable && (card->enable != ISAPNP_CARD_NO_KEY) && (card->state == PNP_STATE_WAIT_FOR_KEY)) card->state = PNP_STATE_SLEEP; card = card->next; } } } else { dev->key_pos = 0; } } } static void isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg, uint8_t val) { uint16_t io_addr; uint16_t reset_cards = 0; isapnp_log("ISAPnP: write_common(%02X, %02X)\n", reg, val); switch (reg) { case 0x00: /* Set RD_DATA Port */ isapnp_set_read_data((val << 2) | 3, dev); isapnp_log("ISAPnP: Read data port set to %04X\n", dev->read_data_addr); break; case 0x02: /* Config Control */ if (val & 0x01) { isapnp_log("ISAPnP: Reset\n"); card = dev->first_card; while (card) { ld = card->first_ld; while (ld) { if (card->state != PNP_STATE_WAIT_FOR_KEY) { isapnp_reset_ld_regs(ld); isapnp_device_config_changed(card, ld); reset_cards++; } ld = ld->next; } card = card->next; } if (reset_cards != 0) { dev->current_ld = NULL; dev->current_ld_card = NULL; dev->isolated_card = NULL; } } if (val & 0x02) { isapnp_log("ISAPnP: Return to WAIT_FOR_KEY\n"); card = dev->first_card; while (card) { card->state = PNP_STATE_WAIT_FOR_KEY; card = card->next; } } if (val & 0x04) { isapnp_log("ISAPnP: Reset CSN\n"); card = dev->first_card; while (card) { isapnp_set_csn(card, 0); card = card->next; } } break; case 0x03: /* Wake[CSN] */ isapnp_log("ISAPnP: Wake[%02X]\n", val); card = dev->first_card; while (card) { if (card->csn == val) { card->rom_pos = 0; card->id_checksum = isapnp_init_key[0]; if (card->state == PNP_STATE_SLEEP) card->state = (val == 0) ? PNP_STATE_ISOLATION : PNP_STATE_CONFIG; } else { card->state = PNP_STATE_SLEEP; } card = card->next; } break; case 0x06: /* Card Select Number */ if (dev->isolated_card) { isapnp_log("ISAPnP: Set CSN %02X\n", val); isapnp_set_csn(dev->isolated_card, val); dev->isolated_card->state = PNP_STATE_CONFIG; dev->isolated_card = NULL; } else { isapnp_log("ISAPnP: Set CSN %02X but no card is isolated\n", val); } break; case 0x07: /* Logical Device Number */ CHECK_CURRENT_CARD(); card->ld = val; ld = card->first_ld; while (ld) { if (ld->number == val) { isapnp_log("ISAPnP: Select CSN %02X device %02X\n", card->csn, val); dev->current_ld_card = card; dev->current_ld = ld; break; } ld = ld->next; } if (!ld) isapnp_log("ISAPnP: CSN %02X has no device %02X\n", card->csn, val); break; case 0x30: /* Activate */ CHECK_CURRENT_LD(); isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (val & 0x01) ? "A" : "Dea", card->csn, ld->number); ld->regs[reg] = val & 0x01; isapnp_device_config_changed(card, ld); break; case 0x31: /* I/O Range Check */ CHECK_CURRENT_LD(); for (uint8_t i = 0; i < 8; i++) { if (!ld->io_len[i]) continue; io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; if (ld->regs[reg] & 0x02) io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); if (val & 0x02) io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); } ld->regs[reg] = val & 0x03; isapnp_device_config_changed(card, ld); break; case 0x20 ... 0x2f: case 0x38 ... 0x3f: case 0xa9 ... 0xff: vendor_defined: CHECK_CURRENT_CARD(); isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld ? ld->number : -1); if (card->write_vendor_reg) card->write_vendor_reg(ld ? ld->number : -1, reg, val, card->priv); break; default: if (reg >= 0x40) { CHECK_CURRENT_LD(); isapnp_log("ISAPnP: Write %02X to register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld->number); switch (reg) { case 0x42: case 0x4a: case 0x52: case 0x5a: case 0x7a: case 0x84: case 0x94: case 0xa4: /* Read-only memory range length / upper limit bit. */ val = (val & 0xfe) | (ld->regs[reg] & 0x01); break; case 0x60: case 0x62: case 0x64: case 0x66: case 0x68: case 0x6a: case 0x6c: case 0x6e: /* Discard upper address bits if this I/O range can only decode 10-bit. */ if (!(ld->io_16bit & (1 << ((reg >> 1) & 0x07)))) val &= 0x03; break; case 0x71: case 0x73: /* Limit IRQ types to supported ones. */ if ((val & 0x01) && !(ld->irq_types & ((reg == 0x71) ? 0x0c : 0xc0))) /* level, not supported = force edge */ val &= ~0x01; else if (!(val & 0x01) && !(ld->irq_types & ((reg == 0x71) ? 0x03 : 0x30))) /* edge, not supported = force level */ val |= 0x01; if ((val & 0x02) && !(ld->irq_types & ((reg == 0x71) ? 0x05 : 0x50))) /* high, not supported = force low */ val &= ~0x02; else if (!(val & 0x02) && !(ld->irq_types & ((reg == 0x71) ? 0x0a : 0xa0))) /* low, not supported = force high */ val |= 0x02; break; default: break; } ld->regs[reg] = val; isapnp_device_config_changed(card, ld); } break; } } static void isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) { isapnp_t *dev = (isapnp_t *) priv; isapnp_card_t *card = NULL; if (!card) { card = dev->first_card; while (card) { if (card->enable && (card->state == PNP_STATE_CONFIG)) break; card = card->next; } } isapnp_log("ISAPnP: write_data(%02X) => ", val); isapnp_write_common(dev, card, dev->current_ld, dev->reg, val); } static void * isapnp_init(UNUSED(const device_t *info)) { isapnp_t *dev = (isapnp_t *) malloc(sizeof(isapnp_t)); memset(dev, 0, sizeof(isapnp_t)); io_sethandler(0x279, 1, NULL, NULL, NULL, isapnp_write_addr, NULL, NULL, dev); io_sethandler(0xa79, 1, NULL, NULL, NULL, isapnp_write_data, NULL, NULL, dev); return dev; } static void isapnp_close(void *priv) { isapnp_t *dev = (isapnp_t *) priv; isapnp_card_t *card = dev->first_card; isapnp_card_t *next_card; isapnp_device_t *ld; isapnp_device_t *next_ld; while (card) { ld = card->first_ld; while (ld) { next_ld = ld->next; free(ld); ld = next_ld; } next_card = card->next; free(card); card = next_card; } io_removehandler(0x279, 1, NULL, NULL, NULL, isapnp_write_addr, NULL, NULL, dev); io_removehandler(0xa79, 1, NULL, NULL, NULL, isapnp_write_data, NULL, NULL, dev); free(dev); } void * isapnp_add_card(uint8_t *rom, uint16_t rom_size, void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv), void (*csn_changed)(uint8_t csn, void *priv), uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv), void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv), void *priv) { isapnp_t *dev = (isapnp_t *) device_get_priv(&isapnp_device); if (!dev) dev = (isapnp_t *) device_add(&isapnp_device); isapnp_card_t *card = (isapnp_card_t *) malloc(sizeof(isapnp_card_t)); memset(card, 0, sizeof(isapnp_card_t)); card->enable = 1; card->priv = priv; card->config_changed = config_changed; card->csn_changed = csn_changed; card->read_vendor_reg = read_vendor_reg; card->write_vendor_reg = write_vendor_reg; if (!dev->first_card) { dev->first_card = card; } else { isapnp_card_t *prev_card = dev->first_card; while (prev_card->next) prev_card = prev_card->next; prev_card->next = card; } if (rom && rom_size) isapnp_update_card_rom(card, rom, rom_size); return card; } void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size) { isapnp_card_t *card = (isapnp_card_t *) priv; card->rom = rom; card->rom_size = rom_size; /* Parse resources in ROM to allocate logical devices, and determine the state of read-only register bits. */ #ifdef ENABLE_ISAPNP_LOG uint16_t vendor = (card->rom[0] << 8) | card->rom[1]; isapnp_log("ISAPnP: Parsing ROM resources for card %c%c%c%02X%02X (serial %08X)\n", '@' + ((vendor >> 10) & 0x1f), '@' + ((vendor >> 5) & 0x1f), '@' + (vendor & 0x1f), card->rom[2], card->rom[3], (card->rom[7] << 24) | (card->rom[6] << 16) | (card->rom[5] << 8) | card->rom[4]); const char *df_priority[] = { "good", "acceptable", "sub-optimal", "unknown priority" }; const char *mem_control[] = { "8-bit", "16-bit", "8/16-bit", "32-bit" }; const char *dma_transfer[] = { "8-bit", "8/16-bit", "16-bit", "unknown" }; const char *dma_speed[] = { "compatibility", "Type A", "Type B", "Type F" }; #endif uint16_t i = 9; uint8_t existing = 0; uint8_t ldn = 0; uint8_t res; uint8_t in_df = 0; uint8_t irq = 0; uint8_t dma = 0; uint8_t io = 0; uint8_t mem_range = 0; uint8_t mem_range_32 = 0; uint8_t irq_df = 0; uint8_t dma_df = 0; uint8_t io_df = 0; uint8_t mem_range_df = 0; uint8_t mem_range_32_df = 0; uint32_t len; isapnp_device_t *ld = NULL; isapnp_device_t *prev_ld = NULL; /* Check if this is an existing card which already has logical devices. Any new logical devices will be added to the list after existing ones. Removed LDs are not flushed as we may end up with an invalid ROM. */ existing = !!card->first_ld; /* Iterate through ROM resources. */ while (i < card->rom_size) { if (card->rom[i] & 0x80) { /* large resource */ res = card->rom[i] & 0x7f; len = (card->rom[i + 2] << 8) | card->rom[i + 1]; switch (res) { case 0x01: /* memory range */ case 0x05: /* 32-bit memory range */ if (res == 0x01) { if (!ld) { isapnp_log("ISAPnP: >>%s Memory descriptor with no logical device\n", in_df ? ">" : ""); break; } if (mem_range > 3) { isapnp_log("ISAPnP: >>%s Memory descriptor overflow (%d)\n", in_df ? ">" : "", mem_range++); break; } isapnp_log("ISAPnP: >>%s Memory range %d with %d bytes at %06X-%06X, align %d", in_df ? ">" : "", mem_range, *((uint16_t *) &card->rom[i + 10]) << 8, *((uint16_t *) &card->rom[i + 4]) << 8, ((card->rom[i + 3] & 0x4) ? 0 : (*((uint16_t *) &card->rom[i + 4]) << 8)) + (*((uint16_t *) &card->rom[i + 6]) << 8), (*((uint16_t *) &card->rom[i + 8]) + 1) << 16); res = 1 << mem_range; mem_range++; } else { if (!ld) { isapnp_log("ISAPnP: >>%s 32-bit memory descriptor with no logical device\n", in_df ? ">" : ""); break; } if (mem_range_32 > 3) { isapnp_log("ISAPnP: >>%s 32-bit memory descriptor overflow (%d)\n", in_df ? ">" : "", mem_range_32++); break; } isapnp_log("ISAPnP: >>%s 32-bit memory range %d with %d bytes at %08X-%08X, align %d", in_df ? ">" : "", mem_range_32, *((uint32_t *) &card->rom[i + 16]) << 8, *((uint32_t *) &card->rom[i + 4]) << 8, ((card->rom[i + 3] & 0x4) ? 0 : (*((uint32_t *) &card->rom[i + 4]) << 8)) + (*((uint32_t *) &card->rom[i + 8]) << 8), *((uint32_t *) &card->rom[i + 12])); res = 1 << (4 + mem_range_32); mem_range_32++; } #ifdef ENABLE_ISAPNP_LOG isapnp_log(" bytes, %swritable, %sread cacheable, %s, %s, %sshadowable, %sexpansion ROM\n", (card->rom[i + 3] & 0x01) ? "not " : "", (card->rom[i + 3] & 0x02) ? "not " : "", (card->rom[i + 3] & 0x04) ? "upper limit" : "range length", mem_control[(card->rom[i + 3] >> 3) & 0x03], (card->rom[i + 3] & 0x20) ? "not " : "", (card->rom[i + 3] & 0x40) ? "not " : ""); #endif if (card->rom[i + 3] & 0x4) ld->mem_upperlimit |= res; else ld->mem_upperlimit &= ~res; break; #ifdef ENABLE_ISAPNP_LOG case 0x02: /* ANSI identifier */ res = card->rom[i + 3 + len]; card->rom[i + 3 + len] = '\0'; isapnp_log("ISAPnP: >%s ANSI identifier: \"%s\"\n", ldn ? ">" : "", &card->rom[i + 3]); card->rom[i + 3 + len] = res; break; #endif default: isapnp_log("ISAPnP: >%s%s Large resource %02X (length %d)\n", ldn ? ">" : "", in_df ? ">" : "", res, (card->rom[i + 2] << 8) | card->rom[i + 1]); break; } i += 3; /* header */ } else { /* small resource */ res = (card->rom[i] >> 3) & 0x0f; len = card->rom[i] & 0x07; switch (res) { #ifdef ENABLE_ISAPNP_LOG case 0x01: /* PnP version */ isapnp_log("ISAPnP: > PnP version %d.%d, vendor-specific version %02X\n", card->rom[i + 1] >> 4, card->rom[i + 1] & 0x0f, card->rom[i + 2]); break; #endif case 0x02: /* logical device */ #ifdef ENABLE_ISAPNP_LOG vendor = (card->rom[i + 1] << 8) | card->rom[i + 2]; isapnp_log("ISAPnP: > Logical device %02X: %c%c%c%02X%02X\n", ldn, '@' + ((vendor >> 10) & 0x1f), '@' + ((vendor >> 5) & 0x1f), '@' + (vendor & 0x1f), card->rom[i + 3], card->rom[i + 4]); #endif /* We're done with the previous logical device. */ if (ld && !existing) isapnp_reset_ld_regs(ld); /* Look for an existing logical device with this number, and create one if none exist. */ if (existing) { ld = card->first_ld; while (ld && (ld->number != ldn)) ld = ld->next; } if (ld && (ld->number == ldn)) { /* Reset some logical device state. */ ld->mem_upperlimit = ld->io_16bit = ld->irq_types = 0; memset(ld->io_len, 0, sizeof(ld->io_len)); } else { /* Create logical device. */ ld = (isapnp_device_t *) malloc(sizeof(isapnp_device_t)); memset(ld, 0, sizeof(isapnp_device_t)); /* Add to end of list. */ prev_ld = card->first_ld; if (prev_ld) { while (prev_ld->next) prev_ld = prev_ld->next; prev_ld->next = ld; } else { card->first_ld = ld; } } /* Set and increment logical device number. */ ld->number = ldn++; /* Start the position counts over. */ irq = dma = io = mem_range = mem_range_32 = irq_df = dma_df = io_df = mem_range_df = mem_range_32_df = 0; break; #ifdef ENABLE_ISAPNP_LOG case 0x03: /* compatible device ID */ if (!ld) { isapnp_log("ISAPnP: >> Compatible device ID with no logical device\n"); break; } vendor = (card->rom[i + 1] << 8) | card->rom[i + 2]; isapnp_log("ISAPnP: >> Compatible device ID: %c%c%c%02X%02X\n", '@' + ((vendor >> 10) & 0x1f), '@' + ((vendor >> 5) & 0x1f), '@' + (vendor & 0x1f), card->rom[i + 3], card->rom[i + 4]); break; #endif case 0x04: /* IRQ */ if (!ld) { isapnp_log("ISAPnP: >>%s IRQ descriptor with no logical device\n", in_df ? ">" : ""); break; } if (irq > 1) { isapnp_log("ISAPnP: >>%s IRQ descriptor overflow (%d)\n", in_df ? ">" : "", irq++); break; } if (len == 2) /* default */ res = 0x01; /* high true edge sensitive */ else /* specific */ res = card->rom[i + 3] & 0x0f; isapnp_log("ISAPnP: >>%s IRQ index %d with mask %04X, types %01X\n", in_df ? ">" : "", irq, *((uint16_t *) &card->rom[i + 1]), res); ld->irq_types &= ~(0x0f << (4 * irq)); ld->irq_types |= res << (4 * irq); irq++; break; #ifdef ENABLE_ISAPNP_LOG case 0x05: /* DMA */ isapnp_log("ISAPnP: >>%s DMA index %d with mask %02X, %s, %sbus master, %scount by byte, %scount by word, %s speed\n", in_df ? ">" : "", dma++, card->rom[i + 1], dma_transfer[card->rom[i + 2] & 3], (card->rom[i + 2] & 0x04) ? "not " : "", (card->rom[i + 2] & 0x08) ? "not " : "", (card->rom[i + 2] & 0x10) ? "not " : "", dma_speed[(card->rom[i + 2] >> 5) & 3]); break; #endif case 0x06: /* start dependent function */ if (!ld) { isapnp_log("ISAPnP: >> Start dependent function with no logical device\n"); break; } #ifdef ENABLE_ISAPNP_LOG isapnp_log("ISAPnP: >> Start dependent function: %s\n", df_priority[(len < 1) ? 1 : (card->rom[i + 1] & 3)]); #endif if (in_df) { /* We're in a dependent function and this is the next one starting. Walk positions back to the saved values. */ irq = irq_df; dma = dma_df; io = io_df; mem_range = mem_range_df; mem_range_32 = mem_range_32_df; } else { /* Save current positions to restore at the next DF. */ irq_df = irq; dma_df = dma; io_df = io; mem_range_df = mem_range; mem_range_32_df = mem_range_32; in_df = 1; } break; case 0x07: /* end dependent function */ isapnp_log("ISAPnP: >> End dependent function\n"); in_df = 0; break; case 0x08: /* I/O port */ if (!ld) { isapnp_log("ISAPnP: >>%s I/O descriptor with no logical device\n", in_df ? ">" : ""); break; } if (io > 7) { isapnp_log("ISAPnP: >>%s I/O descriptor overflow (%d)\n", in_df ? ">" : "", io++); break; } isapnp_log("ISAPnP: >>%s I/O range %d with %d ports at %04X-%04X, align %d, %d-bit decode\n", in_df ? ">" : "", io, card->rom[i + 7], *((uint16_t *) &card->rom[i + 2]), *((uint16_t *) &card->rom[i + 4]), card->rom[i + 6], (card->rom[i + 1] & 0x01) ? 16 : 10); if (card->rom[i + 1] & 0x01) ld->io_16bit |= 1 << io; else ld->io_16bit &= ~(1 << io); if (card->rom[i + 7] > ld->io_len[io]) ld->io_len[io] = card->rom[i + 7]; io++; break; case 0x0f: /* end tag */ /* Calculate checksum. */ res = 0x00; for (uint16_t j = 9; j <= i; j++) res += card->rom[j]; card->rom[i + 1] = -res; isapnp_log("ISAPnP: End card resources (checksum %02X)\n", card->rom[i + 1]); /* Stop parsing here. */ card->rom_size = i + 2; break; default: isapnp_log("ISAPnP: >%s%s Small resource %02X (length %d)\n", ldn ? ">" : "", in_df ? ">" : "", res, card->rom[i] & 0x07); break; } i++; /* header */ } i += len; /* specified length */ } /* We're done with the last logical device. */ if (ld && !existing) isapnp_reset_ld_regs(ld); } void isapnp_enable_card(void *priv, uint8_t enable) { isapnp_t *dev = (isapnp_t *) device_get_priv(&isapnp_device); if (!dev) return; /* Look for a matching card. */ isapnp_card_t *card = dev->first_card; while (card) { if (card == priv) { /* Enable or disable the card. */ if (!!enable ^ !!card->enable) card->state = (enable == ISAPNP_CARD_FORCE_CONFIG) ? PNP_STATE_CONFIG : PNP_STATE_WAIT_FOR_KEY; card->enable = enable; /* Invalidate other references if we're disabling this card. */ if (!card->enable) { if (dev->isolated_card == card) dev->isolated_card = NULL; if (dev->current_ld_card == card) { dev->current_ld = NULL; dev->current_ld_card = NULL; } } break; } card = card->next; } } void isapnp_set_csn(void *priv, uint8_t csn) { isapnp_card_t *card = (isapnp_card_t *) priv; card->csn = csn; if (card->csn_changed) card->csn_changed(card->csn, card->priv); } uint8_t isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg) { isapnp_card_t *card = (isapnp_card_t *) priv; isapnp_device_t *ld = card->first_ld; while (ld) { if (ld->number == ldn) break; ld = ld->next; } return isapnp_read_common(device_get_priv(&isapnp_device), card, ld, reg); } void isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val) { isapnp_card_t *card = (isapnp_card_t *) priv; isapnp_device_t *ld = card->first_ld; while (ld) { if (ld->number == ldn) break; ld = ld->next; } isapnp_write_common(device_get_priv(&isapnp_device), card, ld, reg, val); } void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config) { isapnp_card_t *card = (isapnp_card_t *) priv; isapnp_device_t *ld = card->first_ld; /* Look for a logical device with this number. */ while (ld && (ld->number != ldn)) ld = ld->next; if (!ld) /* none found */ return; ld->defaults = config; } void isapnp_reset_card(void *priv) { isapnp_card_t *card = (isapnp_card_t *) priv; isapnp_device_t *ld = card->first_ld; /* Reset all logical devices. */ while (ld) { /* Reset the logical device's configuration. */ isapnp_reset_ld_config(ld); isapnp_device_config_changed(card, ld); ld = ld->next; } } void isapnp_reset_device(void *priv, uint8_t ldn) { isapnp_card_t *card = (isapnp_card_t *) priv; isapnp_device_t *ld = card->first_ld; /* Look for a logical device with this number. */ while (ld && (ld->number != ldn)) ld = ld->next; if (!ld) /* none found */ return; /* Reset the logical device's configuration. */ isapnp_reset_ld_config(ld); isapnp_device_config_changed(card, ld); } static const device_t isapnp_device = { .name = "ISA Plug and Play", .internal_name = "isapnp", .flags = 0, .local = 0, .init = isapnp_init, .close = isapnp_close, .reset = NULL, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL };