📄 eepro100.c.svn-base
字号:
PCI_CONFIG_32(PCI_BASE_ADDRESS_1, PCI_ADDRESS_SPACE_IO);#if 0 /* Flash Memory Mapped Base Address */ PCI_CONFIG_32(PCI_BASE_ADDRESS_2, 0xfffe0000 | PCI_ADDRESS_SPACE_MEM);#endif#endif /* Expansion ROM Base Address (depends on boot disable!!!) */ PCI_CONFIG_32(0x30, 0x00000000); /* Capability Pointer */ PCI_CONFIG_8(0x34, 0xdc); /* Interrupt Pin */ PCI_CONFIG_8(0x3d, 1); // interrupt pin 0 /* Minimum Grant */ PCI_CONFIG_8(0x3e, 0x08); /* Maximum Latency */ PCI_CONFIG_8(0x3f, 0x18); /* Power Management Capabilities / Next Item Pointer / Capability ID */ PCI_CONFIG_32(0xdc, 0x7e210001); switch (device) { case i82551: //~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209); PCI_CONFIG_8(PCI_REVISION_ID, 0x0f); break; case i82557B: PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229); PCI_CONFIG_8(PCI_REVISION_ID, 0x02); break; case i82557C: PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229); PCI_CONFIG_8(PCI_REVISION_ID, 0x03); break; case i82558B: PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229); PCI_CONFIG_16(PCI_STATUS, 0x2810); PCI_CONFIG_8(PCI_REVISION_ID, 0x05); break; case i82559C: PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229); PCI_CONFIG_16(PCI_STATUS, 0x2810); //~ PCI_CONFIG_8(PCI_REVISION_ID, 0x08); break; case i82559ER: //~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209); PCI_CONFIG_16(PCI_STATUS, 0x2810); PCI_CONFIG_8(PCI_REVISION_ID, 0x09); break; //~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1029); //~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1030); /* 82559 InBusiness 10/100 */ default: logout("Device %X is undefined!\n", device); } if (device == i82557C || device == i82558B || device == i82559C) { logout("Get device id and revision from EEPROM!!!\n"); }}static void nic_selective_reset(EEPRO100State * s){ size_t i; uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom); //~ eeprom93xx_reset(s->eeprom); memcpy(eeprom_contents, s->macaddr, 6); eeprom_contents[0xa] = 0x4000; uint16_t sum = 0; for (i = 0; i < EEPROM_SIZE - 1; i++) { sum += eeprom_contents[i]; } eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum; memset(s->mem, 0, sizeof(s->mem)); uint32_t val = BIT(21); memcpy(&s->mem[SCBCtrlMDI], &val, sizeof(val)); assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default)); memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));}static void nic_reset(void *opaque){ EEPRO100State *s = (EEPRO100State *) opaque; logout("%p\n", s); static int first; if (!first) { first = 1; } nic_selective_reset(s);}#if defined(DEBUG_EEPRO100)static const char *reg[PCI_IO_SIZE / 4] = { "Command/Status", "General Pointer", "Port", "EEPROM/Flash Control", "MDI Control", "Receive DMA Byte Count", "Flow control register", "General Status/Control"};static char *regname(uint32_t addr){ static char buf[16]; if (addr < PCI_IO_SIZE) { const char *r = reg[addr / 4]; if (r != 0) { sprintf(buf, "%s+%u", r, addr % 4); } else { sprintf(buf, "0x%02x", addr); } } else { sprintf(buf, "??? 0x%08x", addr); } return buf;}#endif /* DEBUG_EEPRO100 */#if 0static uint16_t eepro100_read_status(EEPRO100State * s){ uint16_t val = s->status; logout("val=0x%04x\n", val); return val;}static void eepro100_write_status(EEPRO100State * s, uint16_t val){ logout("val=0x%04x\n", val); s->status = val;}#endif/***************************************************************************** * * Command emulation. * ****************************************************************************/#if 0static uint16_t eepro100_read_command(EEPRO100State * s){ uint16_t val = 0xffff; //~ logout("val=0x%04x\n", val); return val;}#endif/* Commands that can be put in a command list entry. */enum commands { CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, CmdTx = 4, CmdTDR = 5, /* load microcode */ CmdDump = 6, CmdDiagnose = 7, /* And some extra flags: */ CmdSuspend = 0x4000, /* Suspend after completion. */ CmdIntr = 0x2000, /* Interrupt after completion. */ CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */};static cu_state_t get_cu_state(EEPRO100State * s){ return ((s->mem[SCBStatus] >> 6) & 0x03);}static void set_cu_state(EEPRO100State * s, cu_state_t state){ s->mem[SCBStatus] = (s->mem[SCBStatus] & 0x3f) + (state << 6);}static ru_state_t get_ru_state(EEPRO100State * s){ return ((s->mem[SCBStatus] >> 2) & 0x0f);}static void set_ru_state(EEPRO100State * s, ru_state_t state){ s->mem[SCBStatus] = (s->mem[SCBStatus] & 0xc3) + (state << 2);}static void dump_statistics(EEPRO100State * s){ /* Dump statistical data. Most data is never changed by the emulation * and always 0, so we first just copy the whole block and then those * values which really matter. * Number of data should check configuration!!! */ cpu_physical_memory_write(s->statsaddr, (uint8_t *) & s->statistics, 64); stl_phys(s->statsaddr + 0, s->statistics.tx_good_frames); stl_phys(s->statsaddr + 36, s->statistics.rx_good_frames); stl_phys(s->statsaddr + 48, s->statistics.rx_resource_errors); stl_phys(s->statsaddr + 60, s->statistics.rx_short_frame_errors); //~ stw_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames); //~ stw_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames); //~ missing("CU dump statistical counters");}static void eepro100_cu_command(EEPRO100State * s, uint8_t val){ eepro100_tx_t tx; uint32_t cb_address; switch (val) { case CU_NOP: /* No operation. */ break; case CU_START: if (get_cu_state(s) != cu_idle) { /* Intel documentation says that CU must be idle for the CU * start command. Intel driver for Linux also starts the CU * from suspended state. */ logout("CU state is %u, should be %u\n", get_cu_state(s), cu_idle); //~ assert(!"wrong CU state"); } set_cu_state(s, cu_active); s->cu_offset = s->pointer; next_command: cb_address = s->cu_base + s->cu_offset; cpu_physical_memory_read(cb_address, (uint8_t *) & tx, sizeof(tx)); uint16_t status = le16_to_cpu(tx.status); uint16_t command = le16_to_cpu(tx.command); logout ("val=0x%02x (cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", val, status, command, tx.link); bool bit_el = ((command & 0x8000) != 0); bool bit_s = ((command & 0x4000) != 0); bool bit_i = ((command & 0x2000) != 0); bool bit_nc = ((command & 0x0010) != 0); //~ bool bit_sf = ((command & 0x0008) != 0); uint16_t cmd = command & 0x0007; s->cu_offset = le32_to_cpu(tx.link); switch (cmd) { case CmdNOp: /* Do nothing. */ break; case CmdIASetup: cpu_physical_memory_read(cb_address + 8, &s->macaddr[0], 6); logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6)); break; case CmdConfigure: cpu_physical_memory_read(cb_address + 8, &s->configuration[0], sizeof(s->configuration)); logout("configuration: %s\n", nic_dump(&s->configuration[0], 16)); break; case CmdMulticastList: //~ missing("multicast list"); break; case CmdTx: (void)0; uint32_t tbd_array = le32_to_cpu(tx.tx_desc_addr); uint16_t tcb_bytes = (le16_to_cpu(tx.tcb_bytes) & 0x3fff); logout ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", tbd_array, tcb_bytes, tx.tbd_count); assert(!bit_nc); //~ assert(!bit_sf); assert(tcb_bytes <= 2600); /* Next assertion fails for local configuration. */ //~ assert((tcb_bytes > 0) || (tbd_array != 0xffffffff)); if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { logout ("illegal values of TBD array address and TCB byte count!\n"); } uint8_t buf[MAX_ETH_FRAME_SIZE + 4]; uint16_t size = 0; uint32_t tbd_address = cb_address + 0x10; assert(tcb_bytes <= sizeof(buf)); while (size < tcb_bytes) { uint32_t tx_buffer_address = ldl_phys(tbd_address); uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); //~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); tbd_address += 8; logout ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", tx_buffer_address, tx_buffer_size); cpu_physical_memory_read(tx_buffer_address, &buf[size], tx_buffer_size); size += tx_buffer_size; } if (tbd_array == 0xffffffff) { /* Simplified mode. Was already handled by code above. */ } else { /* Flexible mode. */ uint8_t tbd_count = 0; if (!(s->configuration[6] & BIT(4))) { /* Extended TCB. */ assert(tcb_bytes == 0); for (; tbd_count < 2; tbd_count++) { uint32_t tx_buffer_address = ldl_phys(tbd_address); uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); tbd_address += 8; logout ("TBD (extended mode): buffer address 0x%08x, size 0x%04x\n", tx_buffer_address, tx_buffer_size); cpu_physical_memory_read(tx_buffer_address, &buf[size], tx_buffer_size); size += tx_buffer_size; if (tx_buffer_el & 1) { break; } } } tbd_address = tbd_array; for (; tbd_count < tx.tbd_count; tbd_count++) { uint32_t tx_buffer_address = ldl_phys(tbd_address); uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); tbd_address += 8; logout ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", tx_buffer_address, tx_buffer_size); cpu_physical_memory_read(tx_buffer_address, &buf[size], tx_buffer_size); size += tx_buffer_size; if (tx_buffer_el & 1) { break; } } } qemu_send_packet(s->vc, buf, size); s->statistics.tx_good_frames++; /* Transmit with bad status would raise an CX/TNO interrupt. * (82557 only). Emulation never has bad status. */ //~ eepro100_cx_interrupt(s); break; case CmdTDR: logout("load microcode\n"); /* Starting with offset 8, the command contains * 64 dwords microcode which we just ignore here. */ break; default: missing("undefined command"); } /* Write new status (success). */ stw_phys(cb_address, status | 0x8000 | 0x2000); if (bit_i) { /* CU completed action. */ eepro100_cx_interrupt(s); } if (bit_el) { /* CU becomes idle. */ set_cu_state(s, cu_idle); eepro100_cna_interrupt(s); } else if (bit_s) { /* CU becomes suspended. */ set_cu_state(s, cu_suspended); eepro100_cna_interrupt(s); } else { /* More entries in list. */ logout("CU list with at least one more entry\n"); goto next_command; } logout("CU list empty\n"); /* List is empty. Now CU is idle or suspended. */ break; case CU_RESUME: if (get_cu_state(s) != cu_suspended) { logout("bad CU resume from CU state %u\n", get_cu_state(s)); /* Workaround for bad Linux eepro100 driver which resumes * from idle state. */ //~ missing("cu resume"); set_cu_state(s, cu_suspended); } if (get_cu_state(s) == cu_suspended) { logout("CU resuming\n"); set_cu_state(s, cu_active); goto next_command; } break; case CU_STATSADDR: /* Load dump counters address. */ s->statsaddr = s->pointer; logout("val=0x%02x (status address)\n", val); break; case CU_SHOWSTATS: /* Dump statistical counters. */ dump_statistics(s); break; case CU_CMD_BASE: /* Load CU base. */ logout("val=0x%02x (CU base address)\n", val); s->cu_base = s->pointer; break; case CU_DUMPSTATS: /* Dump and reset statistical counters. */ dump_statistics(s); memset(&s->statistics, 0, sizeof(s->statistics)); break; case CU_SRESUME: /* CU static resume. */ missing("CU static resume"); break; default: missing("Undefined CU command"); }}static void eepro100_ru_command(EEPRO100State * s, uint8_t val){ switch (val) { case RU_NOP: /* No operation. */ break; case RX_START: /* RU start. */ if (get_ru_state(s) != ru_idle) { logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle); //~ assert(!"wrong RU state"); } set_ru_state(s, ru_ready); s->ru_offset = s->pointer; logout("val=0x%02x (rx start)\n", val); break; case RX_RESUME: /* Restart RU. */ if (get_ru_state(s) != ru_suspended) { logout("RU state is %u, should be %u\n", get_ru_state(s), ru_suspended); //~ assert(!"wrong RU state"); } set_ru_state(s, ru_ready); break; case RX_ADDR_LOAD: /* Load RU base. */ logout("val=0x%02x (RU base address)\n", val); s->ru_base = s->pointer; break; default: logout("val=0x%02x (undefined RU command)\n", val); missing("Undefined SU command"); }}static void eepro100_write_command(EEPRO100State * s, uint8_t val){ eepro100_ru_command(s, val & 0x0f); eepro100_cu_command(s, val & 0xf0); if ((val) == 0) { logout("val=0x%02x\n", val); } /* Clear command byte after command was accepted. */ s->mem[SCBCmd] = 0;}/***************************************************************************** * * EEPROM emulation. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -