From 8919ce5645fd0fba7e470ac72fa76553934aea6a Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Tue, 4 Sep 2018 16:26:58 +0700 Subject: interpro: notworking -> networking (#3815) * interpro: notworking -> networking These changes combine to make InterPro networking work on Windows with the TAP-Windows6 driver. * osdnet: add a receive delay (1 frame) after transmit to avoid a time-travel problem * taptun: pad short Ethernet frames and append FCS (Windows-only until Linux taptun behaviour is verified) * clipper: fix bugs in carry flag handling, prefer sign bit for tests * i82586: fix transmit bug, handle reset * networking: delayed transmit/receive A second attempt to fix networking on InterPro systems, by introducing somewhat realistic delays into network transmit and receive paths. This version works by adding functions to device_network_interface which enable a device to be informed when the transmit or receive completes. The delay is only crudely approximated based on the specified bandwidth and the number of bytes being transmitted, but it should be good enough in practice. Existing drivers should not be impacted by these changes; overriding the new functions (and no longer overriding recv_cb) is necessary to obtain the new behaviour. Changes from the previous commit: * i82586: improve interrupt handling, implement delayed transmit/receive behaviour * dinetwork: add transmit/receive delay timers, handlers and logic * osdnet: remove receive delay, add the ability to start the receive timer --- src/devices/machine/i82586.cpp | 272 ++++++++++++++++++++++---------------- src/devices/machine/i82586.h | 20 ++- src/emu/dinetwork.cpp | 44 +++++- src/emu/dinetwork.h | 15 +++ src/osd/modules/netdev/taptun.cpp | 40 +++++- src/osd/osdnet.cpp | 11 +- src/osd/osdnet.h | 2 +- 7 files changed, 276 insertions(+), 128 deletions(-) diff --git a/src/devices/machine/i82586.cpp b/src/devices/machine/i82586.cpp index 4e55b61b8bd..f728d1f183f 100644 --- a/src/devices/machine/i82586.cpp +++ b/src/devices/machine/i82586.cpp @@ -42,7 +42,7 @@ #include "logmacro.h" -// disable FCS insertion (on transmit) and checking (on receive) because pcap doesn't expose them +// disable FCS insertion on transmit #define I82586_FCS 0 ALLOW_SAVE_TYPE(i82586_base_device::cu_state); @@ -112,46 +112,53 @@ CFG_PARAMS[] = }; i82586_base_device::i82586_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endian, u8 datawidth, u8 addrwidth) - : device_t(mconfig, type, tag, owner, clock), - device_memory_interface(mconfig, *this), - device_network_interface(mconfig, *this, 10.0f), - m_space_config("shared", endian, datawidth, addrwidth), - m_out_irq(*this), - m_cx(false), - m_fr(false), - m_cna(false), - m_rnr(false), - m_irq_state(false), - m_initialised(false), - m_cu_state(CU_IDLE), - m_ru_state(RU_IDLE), - m_scp_address(SCP_ADDRESS), - m_lb_length(0) -{} + : device_t(mconfig, type, tag, owner, clock) + , device_memory_interface(mconfig, *this) + , device_network_interface(mconfig, *this, 10.0f) + , m_space_config("shared", endian, datawidth, addrwidth) + , m_out_irq(*this) + , m_cx(false) + , m_fr(false) + , m_cna(false) + , m_rnr(false) + , m_initialised(false) + , m_irq_assert(1) + , m_cu_state(CU_IDLE) + , m_ru_state(RU_IDLE) + , m_scp_address(SCP_ADDRESS) + , m_lb_length(0) +{ +} i82586_device::i82586_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i82586_base_device(mconfig, I82586, tag, owner, clock, ENDIANNESS_LITTLE, 16, 24) -{} +{ +} i82596_device::i82596_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endian, u8 datawidth) : i82586_base_device(mconfig, type, tag, owner, clock, endian, datawidth, 32) -{} +{ +} i82596_le16_device::i82596_le16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i82596_device(mconfig, I82596_LE16, tag, owner, clock, ENDIANNESS_LITTLE, 16) -{} +{ +} i82596_be16_device::i82596_be16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i82596_device(mconfig, I82596_BE16, tag, owner, clock, ENDIANNESS_BIG, 16) -{} +{ +} i82596_le32_device::i82596_le32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i82596_device(mconfig, I82596_LE32, tag, owner, clock, ENDIANNESS_LITTLE, 32) -{} +{ +} i82596_be32_device::i82596_be32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i82596_device(mconfig, I82596_BE32, tag, owner, clock, ENDIANNESS_BIG, 32) -{} +{ +} // shared implementation void i82586_base_device::device_start() @@ -169,7 +176,6 @@ void i82586_base_device::device_start() save_item(NAME(m_fr)); save_item(NAME(m_cna)); save_item(NAME(m_rnr)); - save_item(NAME(m_irq_state)); save_item(NAME(m_initialised)); save_item(NAME(m_cu_state)); @@ -197,7 +203,6 @@ void i82586_base_device::device_reset() m_fr = false; m_cna = false; m_rnr = false; - m_irq_state = false; m_initialised = false; m_cu_state = CU_IDLE; @@ -213,7 +218,6 @@ void i82586_base_device::device_timer(emu_timer &timer, device_timer_id id, int { case CU_TIMER: cu_execute(); - update_scb(); break; case RU_TIMER: @@ -237,7 +241,7 @@ device_memory_interface::space_config_vector i82586_base_device::memory_space_co WRITE_LINE_MEMBER(i82586_base_device::ca) { - LOG("channel attention %s (%s)\n", state ? "asserted" : "deasserted", machine().describe_context()); + LOG("channel attention %s (%s)\n", state ? "asserted" : "cleared", machine().describe_context()); if (state) { @@ -249,7 +253,7 @@ WRITE_LINE_MEMBER(i82586_base_device::ca) } } -void i82586_base_device::recv_cb(u8 *buf, int length) +int i82586_base_device::recv_start_cb(u8 *buf, int length) { switch (m_ru_state) { @@ -264,9 +268,7 @@ void i82586_base_device::recv_cb(u8 *buf, int length) LOG("recv_cb receiving frame length %d\n", length); dump_bytes(buf, length); - ru_execute(buf, length); - - update_scb(); + return ru_execute(buf, length); } break; @@ -275,6 +277,15 @@ void i82586_base_device::recv_cb(u8 *buf, int length) // TODO: accumulate statistics break; } + + return 0; +} + +void i82586_base_device::recv_complete_cb(int result) +{ + ru_complete(result); + + update_scb(); } void i82586_base_device::process_scb() @@ -282,12 +293,20 @@ void i82586_base_device::process_scb() // fetch current command and status m_scb_cs = m_space->read_dword(m_scb_address); + // handle reset + if (m_scb_cs & RESET) + { + LOG("process_scb reset\n"); + + device_reset(); + return; + } + static const char *const CUC_NAME[] = { "NOP", "START", "RESUME", "SUSPEND", "ABORT", "THROTTLE_D", "THROTTLE_I", "reserved" }; static const char *const RUC_NAME[] = { "NOP", "START", "RESUME", "SUSPEND", "ABORT", "reserved", "reserved", "reserved" }; - LOG("process_scb command/status 0x%08x (cuc %s, ruc %s%s)\n", m_scb_cs, + LOG("process_scb command/status 0x%08x (cuc %s, ruc %s)\n", m_scb_cs, CUC_NAME[(m_scb_cs & CUC) >> 24], - RUC_NAME[(m_scb_cs & RUC) >> 20], - m_scb_cs & RESET ? ", reset" : ""); + RUC_NAME[(m_scb_cs & RUC) >> 20]); // clear interrupt flags when acknowledged if (m_scb_cs & ACK_CX) @@ -379,6 +398,8 @@ void i82586_base_device::update_scb() (m_cu_state << 8) | (m_ru_state << 4)); + LOG("update_scb%s%s%s%s\n", m_cx ? " CX" : "", m_fr ? " FR" : "", m_cna ? " CNA" : "", m_rnr ? " RNR" : ""); + // update interrupt status set_irq(m_cx || m_fr || m_cna || m_rnr); } @@ -386,7 +407,8 @@ void i82586_base_device::update_scb() void i82586_base_device::cu_execute() { // fetch the command block command/status - u32 cb_cs = m_space->read_dword(m_cba); + const u32 cb_cs = m_space->read_dword(m_cba); + u16 status = 0; // set busy status m_space->write_dword(m_cba, cb_cs | CB_B); @@ -400,52 +422,57 @@ void i82586_base_device::cu_execute() switch (cb_cs & CB_CMD) { case CB_NOP: - cb_cs |= CB_OK; + status |= CB_OK; break; case CB_IASETUP: if (cu_iasetup()) - cb_cs |= CB_OK; + status |= CB_OK; break; case CB_CONFIGURE: if (cu_configure()) - cb_cs |= CB_OK; + status |= CB_OK; break; case CB_MCSETUP: if (cu_mcsetup()) - cb_cs |= CB_OK; + status |= CB_OK; break; case CB_TRANSMIT: - // always turn on the heartbeat indicator status after a successful transmission; not - // strictly correct, but allows one InterPro 2000 diagnostic to pass if (cu_transmit(cb_cs)) - cb_cs |= CB_OK | CB_S6; + return; break; case CB_TDREFLECT: if (cu_tdreflect()) - cb_cs |= CB_OK; + status |= CB_OK; break; case CB_DUMP: if (cu_dump()) - cb_cs |= CB_OK; + status |= CB_OK; break; case CB_DIAGNOSE: - cb_cs |= CB_OK; + status |= CB_OK; break; } } else // abort status - cb_cs |= CB_A; + status |= CB_A; + // complete command + cu_complete(status); +} + +void i82586_base_device::cu_complete(const u16 status) +{ // clear busy status and set completion status - m_space->write_dword(m_cba, cb_cs | CB_C); + const u32 cb_cs = m_space->read_dword(m_cba); + m_space->write_dword(m_cba, (cb_cs & ~0xffffU) | CB_C | status); // chain to next command if (!(cb_cs & CB_EL)) @@ -479,7 +506,9 @@ void i82586_base_device::cu_execute() LOG("cu_execute complete state %s\n", CU_STATE_NAME[m_cu_state]); // set command executed status - m_cx = (cb_cs & CB_I) && (cb_cs & CB_OK); + m_cx = (cb_cs & CB_I) && (status & CB_OK); + + update_scb(); } bool i82586_base_device::address_filter(u8 *mac) @@ -531,15 +560,12 @@ bool i82586_base_device::address_filter(u8 *mac) // shared helpers void i82586_base_device::set_irq(bool irq) { - if (m_irq_state != irq) - { - m_irq_state = irq; - m_out_irq(m_irq_state ? ASSERT_LINE : CLEAR_LINE); - } - else if (m_irq_state && irq) + if (irq) { - m_out_irq(CLEAR_LINE); - m_out_irq(ASSERT_LINE); + LOG("irq asserted\n"); + + m_out_irq(m_irq_assert); + m_out_irq(!m_irq_assert); } } @@ -747,6 +773,7 @@ void i82586_device::device_start() { i82586_base_device::device_start(); + save_item(NAME(m_rbd_offset)); save_item(NAME(m_cfg_bytes)); } @@ -987,10 +1014,17 @@ bool i82586_device::cu_transmit(u32 command) LOG("cu_transmit sending frame length %d\n", length); dump_bytes(buf, length); - return send(buf, length) == 0; + return send(buf, length) == length; } } +void i82586_base_device::send_complete_cb(int result) +{ + // always turn on the heartbeat indicator status after a successful transmit; not + // strictly correct, but allows one InterPro 2000 diagnostic to pass + cu_complete(result ? (CB_OK | CB_S6) : CB_S9); +} + bool i82586_device::cu_tdreflect() { m_space->write_word(m_cba + 6, TDR_LNK_OK | TDR_TIME); @@ -1036,10 +1070,11 @@ bool i82586_device::address_filter(u8 *mac) return false; } -void i82586_device::ru_execute(u8 *buf, int length) +u16 i82586_device::ru_execute(u8 *buf, int length) { // fetch receive frame descriptor command/status u32 rfd_cs = m_space->read_dword(m_rfd); + u16 status = 0; // current buffer position and bytes remaining int position = 0, remaining = length; @@ -1051,9 +1086,8 @@ void i82586_device::ru_execute(u8 *buf, int length) // set short frame status if (length < cfg_min_frame_length()) - rfd_cs |= RFD_S_SHORT; + status |= RFD_S_SHORT; -#if I82586_FCS // set crc status if (~compute_crc(buf, length, cfg_crc16()) != FCS_RESIDUE) { @@ -1063,15 +1097,14 @@ void i82586_device::ru_execute(u8 *buf, int length) // increment crc error count m_space->write_word(m_scb_address + 8, m_space->read_word(m_scb_address + 8) + 1); - rfd_cs |= RFD_S_CRC; + status |= RFD_S_CRC; } -#endif // TODO: alignment error (crc in misaligned frame), status bit 10 // TODO: increment alignment error counter // fetch initial rbd offset from rfd - u16 rbd_offset = m_space->read_word(m_rfd + 6); + m_rbd_offset = m_space->read_word(m_rfd + 6); if (!cfg_no_src_add_ins()) { @@ -1087,11 +1120,11 @@ void i82586_device::ru_execute(u8 *buf, int length) } // store remaining bytes in receive buffers - while (remaining && rbd_offset != RBD_EMPTY) + while (remaining && m_rbd_offset != RBD_EMPTY) { // fetch the count and address for this buffer - u32 rb_address = m_space->read_dword(m_scb_base + rbd_offset + 4); - u16 rbd_size = m_space->read_word(m_scb_base + rbd_offset + 8); + u32 rb_address = m_space->read_dword(m_scb_base + m_rbd_offset + 4); + u16 rbd_size = m_space->read_word(m_scb_base + m_rbd_offset + 8); // compute number of bytes to store in buffer int actual = remaining > (rbd_size & RB_SIZE) ? (rbd_size & RB_SIZE) : remaining; @@ -1104,17 +1137,17 @@ void i82586_device::ru_execute(u8 *buf, int length) remaining -= actual; // store actual count - m_space->write_word(m_scb_base + rbd_offset + 0, actual | RB_F | (remaining ? 0 : RB_EOF)); + m_space->write_word(m_scb_base + m_rbd_offset + 0, actual | RB_F | (remaining ? 0 : RB_EOF)); // check if buffers exhausted if ((rbd_size & RB_EL)) { - rbd_offset = RBD_EMPTY; + m_rbd_offset = RBD_EMPTY; if (remaining) { // set buffers exhausted status - rfd_cs |= RFD_S_BUFFER; + status |= RFD_S_BUFFER; m_ru_state = RU_NR; m_rnr = true; @@ -1122,28 +1155,33 @@ void i82586_device::ru_execute(u8 *buf, int length) } else // fetch next rbd offset - rbd_offset = m_space->read_word(m_scb_base + rbd_offset + 2); + m_rbd_offset = m_space->read_word(m_scb_base + m_rbd_offset + 2); } if (remaining == 0 || cfg_save_bad_frames()) // set frame received status - rfd_cs |= RFD_C; + status |= RFD_C; // frame received without errors - if (!(rfd_cs & RFD_ERROR_82586)) - { - LOG("ru_execute frame received without error\n"); + if (!(status & RFD_ERROR_82586)) + status |= RFD_OK; - rfd_cs |= RFD_OK; - } + return status; +} + +void i82586_device::ru_complete(const u16 status) +{ + if (status & RFD_OK) + LOG("ru_complete frame received without error\n"); else - LOG("ru_execute frame received with errors status 0x%04x\n", rfd_cs); + LOG("ru_complete frame received with errors status 0x%04x\n", status); - // store status - m_space->write_dword(m_rfd, rfd_cs); + // update receive frame descriptor status + u32 rfd_cs = m_space->read_dword(m_rfd); + m_space->write_dword(m_rfd, (rfd_cs & ~0xffffU) | status); // if we received without error, or we're saving bad frames, advance to the next rfd - if ((rfd_cs & RFD_OK) || cfg_save_bad_frames()) + if ((status & RFD_OK) || cfg_save_bad_frames()) { if (!(rfd_cs & RFD_EL)) { @@ -1151,8 +1189,8 @@ void i82586_device::ru_execute(u8 *buf, int length) m_rfd = m_scb_base + m_space->read_word(m_rfd + 4); // store next free rbd address into rfd - if (rbd_offset != RBD_EMPTY) - m_space->write_word(m_rfd + 6, rbd_offset); + if (m_rbd_offset != RBD_EMPTY) + m_space->write_word(m_rfd + 6, m_rbd_offset); } else { @@ -1172,7 +1210,7 @@ void i82586_device::ru_execute(u8 *buf, int length) } static const char *const RU_STATE_NAME[] = { "IDLE", "SUSPENDED", "NO RESOURCES", nullptr, "READY" }; - LOG("ru_execute complete state %s\n", RU_STATE_NAME[m_ru_state]); + LOG("ru_complete complete state %s\n", RU_STATE_NAME[m_ru_state]); } u32 i82586_device::address(u32 base, int offset, int address, u16 empty) @@ -1191,6 +1229,8 @@ void i82596_device::device_start() save_item(NAME(m_sysbus)); + save_item(NAME(m_irq_assert)); + save_item(NAME(m_rbd_address)); save_item(NAME(m_mac_multi_ia)); } @@ -1267,6 +1307,9 @@ void i82596_device::initialise() break; } + // configure interrupt polarity + m_irq_assert = (m_sysbus & SYSBUS_INT) ? 0 : 1; + // clear iscp busy byte m_space->write_byte(iscp_address, 0); @@ -1652,7 +1695,7 @@ bool i82596_device::cu_transmit(u32 command) LOG("cu_transmit sending frame length %d\n", length); dump_bytes(buf, length); - return send(buf, length) == 0; + return send(buf, length) == length; } } @@ -1743,10 +1786,11 @@ bool i82596_device::address_filter(u8 *mac) return false; } -void i82596_device::ru_execute(u8 *buf, int length) +u16 i82596_device::ru_execute(u8 *buf, int length) { // fetch receive frame descriptor command/status - u32 rfd_cs = m_space->read_dword(m_rfd); + const u32 rfd_cs = m_space->read_dword(m_rfd); + u16 status = 0; // offset into rfd/rbd for linear mode int linear_offset = mode() == MODE_LINEAR ? 4 : 0; @@ -1782,10 +1826,9 @@ void i82596_device::ru_execute(u8 *buf, int length) if (mode() != MODE_82586) m_space->write_dword(m_scb_address + 28 + linear_offset, m_space->read_dword(m_scb_address + 28 + linear_offset) + 1); - rfd_cs |= RFD_S_SHORT; + status |= RFD_S_SHORT; } -#if I82586_FCS // set crc status if (~compute_crc(buf, length, cfg_crc16()) != FCS_RESIDUE) { @@ -1798,19 +1841,18 @@ void i82596_device::ru_execute(u8 *buf, int length) else m_space->write_dword(m_scb_address + 8 + linear_offset, m_space->read_dword(m_scb_address + 8 + linear_offset) + 1); - rfd_cs |= RFD_S_CRC; + status |= RFD_S_CRC; } -#endif // TODO: alignment error (crc in misaligned frame), status bit 10 // TODO: increment alignment error counter // set multicast status if (mode() != MODE_82586 && memcmp(buf, get_mac(), cfg_address_length())) - rfd_cs |= RFD_S_MULTICAST; + status |= RFD_S_MULTICAST; // fetch initial rbd address from rfd - u32 rbd_address = address(m_rfd, 6, 8, RBD_EMPTY); + m_rbd_address = address(m_rfd, 6, 8, RBD_EMPTY); // check for simplified mode if (mode() != MODE_82586 && !(rfd_cs & RFD_SF)) @@ -1839,7 +1881,7 @@ void i82596_device::ru_execute(u8 *buf, int length) m_space->write_word(m_rfd + 8 + linear_offset, actual | RB_F | RB_EOF); // set frame received and truncated frame status - rfd_cs |= RFD_C | (actual < length ? RFD_S_TRUNCATED : 0); + status |= RFD_C | (actual < length ? RFD_S_TRUNCATED : 0); } else LOG("ru_execute discarding %d byte frame exceeding rfd size %d\n", length, rfd_size); @@ -1879,11 +1921,11 @@ void i82596_device::ru_execute(u8 *buf, int length) } // store remaining bytes in receive buffers - while (remaining && rbd_address != RBD_EMPTY) + while (remaining && m_rbd_address != RBD_EMPTY) { // fetch the count and address for this buffer - u32 rb_address = m_space->read_dword(rbd_address + 4 + linear_offset); - u16 rbd_size = m_space->read_word(rbd_address + 8 + linear_offset); + u32 rb_address = m_space->read_dword(m_rbd_address + 4 + linear_offset); + u16 rbd_size = m_space->read_word(m_rbd_address + 8 + linear_offset); // compute number of bytes to store in buffer int actual = remaining > (rbd_size & RB_SIZE) ? (rbd_size & RB_SIZE) : remaining; @@ -1896,17 +1938,17 @@ void i82596_device::ru_execute(u8 *buf, int length) remaining -= actual; // store actual count - m_space->write_word(rbd_address + 0, actual | RB_F | (remaining ? 0 : RB_EOF)); + m_space->write_word(m_rbd_address + 0, actual | RB_F | (remaining ? 0 : RB_EOF)); // check if buffers exhausted if ((rbd_size & RB_EL)) { - rbd_address = RBD_EMPTY; + m_rbd_address = RBD_EMPTY; if (remaining) { // set buffers exhausted status - rfd_cs |= RFD_S_BUFFER; + status |= RFD_S_BUFFER; m_ru_state = mode() == MODE_82586 ? RU_NR : RU_NR_RBD; m_rnr = true; @@ -1914,26 +1956,30 @@ void i82596_device::ru_execute(u8 *buf, int length) } else // fetch next rbd address - rbd_address = address(rbd_address, 2, 4); + m_rbd_address = address(m_rbd_address, 2, 4); } if (remaining == 0 || cfg_save_bad_frames()) // set frame received status - rfd_cs |= RFD_C; + status |= RFD_C; } - // frame received without errors - if (!(rfd_cs & (mode() == MODE_82586 ? RFD_ERROR_82586 : RFD_ERROR))) - { - LOG("ru_execute frame received without error\n"); + if (!(status & (mode() == MODE_82586 ? RFD_ERROR_82586 : RFD_ERROR))) + status |= RFD_OK; - rfd_cs |= RFD_OK; - } + return status; +} + +void i82596_device::ru_complete(const u16 status) +{ + if (status & RFD_OK) + LOG("ru_complete frame received without error\n"); else - LOG("ru_execute frame received with errors status 0x%04x\n", rfd_cs); + LOG("ru_complete frame received with errors status 0x%04x\n", status); // store status - m_space->write_dword(m_rfd, rfd_cs); + const u32 rfd_cs = m_space->read_dword(m_rfd); + m_space->write_dword(m_rfd, (rfd_cs & ~0xffffU) | status); // if we received without error, or we're saving bad frames, advance to the next rfd if ((rfd_cs & RFD_OK) || cfg_save_bad_frames()) @@ -1944,12 +1990,12 @@ void i82596_device::ru_execute(u8 *buf, int length) m_rfd = address(m_rfd, 4, 4); // store next free rbd address into rfd - if (rbd_address != RBD_EMPTY) + if (m_rbd_address != RBD_EMPTY) { if (mode() == MODE_LINEAR) - m_space->write_dword(m_rfd + 8, rbd_address); + m_space->write_dword(m_rfd + 8, m_rbd_address); else - m_space->write_word(m_rfd + 6, rbd_address - m_scb_base); + m_space->write_word(m_rfd + 6, m_rbd_address - m_scb_base); } } else @@ -1970,7 +2016,7 @@ void i82596_device::ru_execute(u8 *buf, int length) } static const char *const RU_STATE_NAME[] = { "IDLE", "SUSPENDED", "NO RESOURCES", nullptr, "READY", nullptr, nullptr, nullptr, nullptr, nullptr, "NO RESOURCES (RFD)", nullptr, "NO RESOURCES (RBD)" }; - LOG("ru_execute complete state %s\n", RU_STATE_NAME[m_ru_state]); + LOG("ru_complete complete state %s\n", RU_STATE_NAME[m_ru_state]); } u32 i82596_device::address(u32 base, int offset, int address, u16 empty) diff --git a/src/devices/machine/i82586.h b/src/devices/machine/i82586.h index e45e46fe4f4..d8d0b794704 100644 --- a/src/devices/machine/i82586.h +++ b/src/devices/machine/i82586.h @@ -158,7 +158,6 @@ public: auto out_irq_cb() { return m_out_irq.bind(); } DECLARE_WRITE_LINE_MEMBER(ca); - virtual void recv_cb(u8 *buf, int length) override; protected: i82586_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endian, u8 datawidth, u8 addrwidth); @@ -170,6 +169,11 @@ protected: virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; virtual space_config_vector memory_space_config() const override; + // device_network_interface overrides + virtual void send_complete_cb(int result) override; + virtual int recv_start_cb(u8 *buf, int length) override; + virtual void recv_complete_cb(int result) override; + // setup and scb virtual void initialise() = 0; virtual void process_scb(); @@ -177,6 +181,7 @@ protected: // command unit virtual void cu_execute(); + virtual void cu_complete(const u16 status); virtual bool cu_iasetup() = 0; virtual bool cu_configure() = 0; virtual bool cu_mcsetup() = 0; @@ -186,7 +191,8 @@ protected: // receive unit virtual bool address_filter(u8 *mac); - virtual void ru_execute(u8 *buf, int length) = 0; + virtual u16 ru_execute(u8 *buf, int length) = 0; + virtual void ru_complete(const u16 status) = 0; // helpers void set_irq(bool irq); @@ -213,8 +219,8 @@ protected: bool m_fr; // frame received bool m_cna; // command unit became inactive bool m_rnr; // receive unit became not ready - bool m_irq_state; bool m_initialised; + int m_irq_assert; // configurable interrupt polarity // receive/command unit state cu_state m_cu_state; // command unit state @@ -279,7 +285,8 @@ protected: // receive unit virtual bool address_filter(u8 *mac) override; - virtual void ru_execute(u8 *buf, int length) override; + virtual u16 ru_execute(u8 *buf, int length) override; + virtual void ru_complete(const u16 status) override; // helpers virtual u32 address(u32 base, int offset, int address) override { return m_scb_base + m_space->read_word(base + offset); } @@ -291,6 +298,7 @@ private: virtual void cfg_set(int offset, u8 data) override { m_cfg_bytes[offset] = data; } u8 m_cfg_bytes[CFG_SIZE]; + u16 m_rbd_offset; // next available receive buffer descriptor }; class i82596_device : public i82586_base_device @@ -336,7 +344,8 @@ protected: // receive unit virtual bool address_filter(u8 *mac) override; - virtual void ru_execute(u8 *buf, int length) override; + virtual u16 ru_execute(u8 *buf, int length) override; + virtual void ru_complete(const u16 status) override; // helpers virtual u32 address(u32 base, int offset, int address) override { return (mode() == MODE_LINEAR) ? m_space->read_dword(base + address) : m_scb_base + m_space->read_word(base + offset); } @@ -357,6 +366,7 @@ private: u8 m_cfg_bytes[CFG_SIZE]; u8 m_sysbus; + u32 m_rbd_address; // next available receive buffer descriptor u64 m_mac_multi_ia; // multi-ia address hash table }; diff --git a/src/emu/dinetwork.cpp b/src/emu/dinetwork.cpp index ee8374a4397..412e37fb060 100644 --- a/src/emu/dinetwork.cpp +++ b/src/emu/dinetwork.cpp @@ -16,14 +16,56 @@ device_network_interface::~device_network_interface() { } +void device_network_interface::interface_pre_start() +{ + m_send_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::send_complete), this)); + m_recv_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::recv_complete), this)); +} + int device_network_interface::send(u8 *buf, int len) const { if(!m_dev) return 0; - return m_dev->send(buf, len); + + // TODO: enable this check when other devices implement delayed transmit + //assert_always(!m_send_timer->enabled(), "attempted to transmit while transmit already in progress"); + + // send the data + int result = m_dev->send(buf, len); + + // schedule transmit complete callback + m_send_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result); + + return result; +} + +TIMER_CALLBACK_MEMBER(device_network_interface::send_complete) +{ + send_complete_cb(param); } void device_network_interface::recv_cb(u8 *buf, int len) { + assert_always(!m_recv_timer->enabled(), "attempted to receive while receive already in progress"); + + // process the received data + int result = recv_start_cb(buf, len); + + if (result) + { + // stop receiving more data from the network + m_dev->stop(); + + // schedule receive complete callback + m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result); + } +} + +TIMER_CALLBACK_MEMBER(device_network_interface::recv_complete) +{ + recv_complete_cb(param); + + // start receiving data from the network again + m_dev->start(); } void device_network_interface::set_promisc(bool promisc) diff --git a/src/emu/dinetwork.h b/src/emu/dinetwork.h index 8cf6ec9c5a7..fa5ee730a87 100644 --- a/src/emu/dinetwork.h +++ b/src/emu/dinetwork.h @@ -11,6 +11,8 @@ public: device_network_interface(const machine_config &mconfig, device_t &device, float bandwidth); virtual ~device_network_interface(); + void interface_pre_start() override; + void set_interface(int id); void set_promisc(bool promisc); void set_mac(const char *mac); @@ -20,14 +22,27 @@ public: int get_interface() const { return m_intf; } int send(u8 *buf, int len) const; + + // TODO: de-virtualise this when existing devices implement delayed receive virtual void recv_cb(u8 *buf, int len); + // delayed transmit/receive handlers + virtual void send_complete_cb(int result) {} + virtual int recv_start_cb(u8 *buf, int len) { return 0; } + virtual void recv_complete_cb(int result) {} + protected: + TIMER_CALLBACK_MEMBER(send_complete); + TIMER_CALLBACK_MEMBER(recv_complete); + bool m_promisc; char m_mac[6]; float m_bandwidth; std::unique_ptr m_dev; int m_intf; + + emu_timer *m_send_timer; + emu_timer *m_recv_timer; }; diff --git a/src/osd/modules/netdev/taptun.cpp b/src/osd/modules/netdev/taptun.cpp index b4d8b963c02..a76718e1ad8 100644 --- a/src/osd/modules/netdev/taptun.cpp +++ b/src/osd/modules/netdev/taptun.cpp @@ -28,6 +28,9 @@ // for some reason this isn't defined in the header, and presumably it changes // with major? versions of the driver - perhaps it should be configurable? #define PRODUCT_TAP_WIN_COMPONENT_ID "tap0901" + +// Ethernet minimum frame length +static constexpr int ETHERNET_MIN_FRAME = 64; #endif class taptun_module : public osd_module, public netdev_module @@ -53,10 +56,10 @@ public: netdev_tap(const char *name, class device_network_interface *ifdev, int rate); ~netdev_tap(); - int send(uint8_t *buf, int len); + int send(uint8_t *buf, int len) override; void set_mac(const char *mac); protected: - int recv_dev(uint8_t **buf); + int recv_dev(uint8_t **buf) override; private: #if defined(WIN32) HANDLE m_handle = INVALID_HANDLE_VALUE; @@ -136,6 +139,35 @@ void netdev_tap::set_mac(const char *mac) } #if defined(WIN32) +static u32 finalise_frame(u8 buf[], u32 length) +{ + /* + * On Windows, the taptun driver receives frames which are shorter + * than the Ethernet minimum. Partly this is because it can't see + * the frame check sequence bytes, but mainly it's because NDIS + * expects the lower level device to add the required padding. + * We do the equivalent padding here (i.e. pad with zeroes to the + * minimum Ethernet length minus FCS), so that devices which check + * for this will not reject these packets. + */ + if (length < ETHERNET_MIN_FRAME - 4) + { + std::fill_n(&buf[length], ETHERNET_MIN_FRAME - length - 4, 0); + + length = ETHERNET_MIN_FRAME - 4; + } + + // compute and append the frame check sequence + const u32 fcs = util::crc32_creator::simple(buf, length); + + buf[length++] = (fcs >> 0) & 0xff; + buf[length++] = (fcs >> 8) & 0xff; + buf[length++] = (fcs >> 16) & 0xff; + buf[length++] = (fcs >> 24) & 0xff; + + return length; +} + int netdev_tap::send(uint8_t *buf, int len) { OVERLAPPED overlapped = {}; @@ -171,7 +203,7 @@ int netdev_tap::recv_dev(uint8_t **buf) // handle unexpected synchronous completion *buf = m_buf; - return bytes_transferred; + return finalise_frame(m_buf, bytes_transferred); } else if (GetLastError() == ERROR_IO_PENDING) m_receive_pending = true; @@ -184,7 +216,7 @@ int netdev_tap::recv_dev(uint8_t **buf) m_receive_pending = false; *buf = m_buf; - return bytes_transferred; + return finalise_frame(m_buf, bytes_transferred); } } diff --git a/src/osd/osdnet.cpp b/src/osd/osdnet.cpp index 90a9297f92d..3e1cb3596e1 100644 --- a/src/osd/osdnet.cpp +++ b/src/osd/osdnet.cpp @@ -38,7 +38,6 @@ class osd_netdev *open_netdev(int id, class device_network_interface *ifdev, int osd_netdev::osd_netdev(class device_network_interface *ifdev, int rate) { m_dev = ifdev; - m_stop = false; m_timer = ifdev->device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(osd_netdev::recv), this)); m_timer->adjust(attotime::from_hz(rate), 0, attotime::from_hz(rate)); } @@ -47,10 +46,14 @@ osd_netdev::~osd_netdev() { } +void osd_netdev::start() +{ + m_timer->enable(true); +} + void osd_netdev::stop() { - m_stop = true; - m_timer->reset(); + m_timer->enable(false); } int osd_netdev::send(uint8_t *buf, int len) @@ -63,7 +66,7 @@ void osd_netdev::recv(void *ptr, int param) uint8_t *buf; int len; //const char atalkmac[] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }; - while((!m_stop) && (len = recv_dev(&buf))) + while(m_timer->enabled() && (len = recv_dev(&buf))) { #if 0 if(buf[0] & 1) diff --git a/src/osd/osdnet.h b/src/osd/osdnet.h index 87a1b56b9f0..226ae2319a9 100644 --- a/src/osd/osdnet.h +++ b/src/osd/osdnet.h @@ -22,6 +22,7 @@ public: }; osd_netdev(class device_network_interface *ifdev, int rate); virtual ~osd_netdev(); + void start(); void stop(); virtual int send(uint8_t *buf, int len); @@ -39,7 +40,6 @@ private: class device_network_interface *m_dev; emu_timer *m_timer; - bool m_stop; }; class osd_netdev *open_netdev(int id, class device_network_interface *ifdev, int rate); -- cgit v1.2.3