// license:BSD-3-Clause // copyright-holders:Patrick Mackinlay /* * AMD Am7990 Local Area Network Controller for Ethernet (LANCE) and Am79C90 * CMOS Local Area Network Controller for Ethernet (C-LANCE). * * Sources: * * http://bitsavers.org/components/amd/Am7990/Am7990.pdf * http://bitsavers.org/components/amd/Am7990/Am79c90.pdf * * TODO * - external loopback * - hp9k/3xx diagnostic failures * * _____ _____ * Vss 1 |* \_/ | 48 Vdd * DAL7 2 | | 47 DAL8 * DAL6 3 | | 46 DAL9 * DAL5 4 | | 45 DAL10 * DAL4 5 | | 44 DAL11 * DAL3 6 | | 43 DAL12 * DAL2 7 | | 42 DAL13 * DAL1 8 | | 41 DAL14 * DAL0 9 | | 40 DAL15 * READ 10 | | 39 A16 * /INTR 11 | | 38 A17 * /DALI 12 | Am79C90 | 37 A18 * /DALI 13 | | 36 A19 * /DAS 14 | | 35 A20 * /BM0,BYTE 15 | | 34 A21 * /BM1,/BUSAKO 16 | | 33 A22 * /HOLD,/BUSRQ 17 | | 32 A23 * ALE,/AS 18 | | 31 RX * /HLDA 19 | | 30 RENA * /CS 20 | | 29 TX * ADR 21 | | 28 CLSN * /READY 22 | | 27 RCLK * /RESET 23 | | 26 TENA * Vss 24 |_____________| 25 TCLK */ #include "emu.h" #include "am79c90.h" #define LOG_GENERAL (1U << 0) #define LOG_REG (1U << 1) #define LOG_INIT (1U << 2) #define LOG_RXTX (1U << 3) #define LOG_FILTER (1U << 4) #define LOG_PACKETS (1U << 5) //#define VERBOSE (LOG_GENERAL|LOG_REG|LOG_INIT|LOG_RXTX|LOG_FILTER|LOG_PACKETS) #include "logmacro.h" DEFINE_DEVICE_TYPE(AM7990, am7990_device, "am7990", "Am7990 LANCE Ethernet Controller") DEFINE_DEVICE_TYPE(AM79C90, am79c90_device, "am79c90", "Am79C90 C-LANCE Ethernet Controller") am7990_device_base::am7990_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, type, tag, owner, clock) , device_network_interface(mconfig, *this, 10.0f) , m_intr_out_cb(*this) , m_dma_in_cb(*this) , m_dma_out_cb(*this) , m_transmit_poll(nullptr) , m_intr_out_state(1) { } am7990_device::am7990_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : am7990_device_base(mconfig, AM7990, tag, owner, clock) { } am79c90_device::am79c90_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : am7990_device_base(mconfig, AM79C90, tag, owner, clock) { } const u8 am7990_device_base::ETH_BROADCAST[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; constexpr attotime am7990_device_base::TX_POLL_PERIOD; void am7990_device_base::device_start() { m_intr_out_cb.resolve_safe(); m_dma_in_cb.resolve_safe(0); m_dma_out_cb.resolve_safe(); m_transmit_poll = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(am7990_device_base::transmit_poll), this)); m_transmit_poll->adjust(TX_POLL_PERIOD, 0, TX_POLL_PERIOD); save_item(NAME(m_rap)); save_item(NAME(m_csr)); save_item(NAME(m_mode)); save_item(NAME(m_logical_addr_filter)); save_item(NAME(m_physical_addr)); save_item(NAME(m_rx_ring_base)); save_item(NAME(m_rx_ring_mask)); save_item(NAME(m_rx_ring_pos)); save_item(NAME(m_rx_md)); save_item(NAME(m_tx_ring_base)); save_item(NAME(m_tx_ring_mask)); save_item(NAME(m_tx_ring_pos)); save_item(NAME(m_tx_md)); save_item(NAME(m_intr_out_state)); save_item(NAME(m_idon)); save_item(NAME(m_lb_buf)); save_item(NAME(m_lb_length)); } void am7990_device_base::device_reset() { m_rap = 0; m_csr[0] = CSR0_STOP; m_csr[3] = 0; m_mode = 0; m_lb_length = 0; m_idon = false; update_interrupts(); } void am7990_device_base::update_interrupts() { if (m_csr[0] & CSR0_INTR) { // assert intr if interrupts are enabled and not asserted if ((m_csr[0] & CSR0_INEA) && m_intr_out_state) { m_intr_out_state = !m_intr_out_state; m_intr_out_cb(m_intr_out_state); LOG("interrupt asserted\n"); } } else { // deassert intr if (!m_intr_out_state) { m_intr_out_state = !m_intr_out_state; m_intr_out_cb(m_intr_out_state); } } } int am7990_device_base::recv_start_cb(u8 *buf, int length) { // check internal loopback if ((m_mode & MODE_LOOP) && (m_mode & MODE_INTL)) { LOGMASKED(LOG_RXTX, "receive internal loopback mode, external packet discarded\n"); return 0; } // discard runt packets if (length < 64) { LOGMASKED(LOG_RXTX, "receive runt packet length %d discarded\n", length); return 0; } return receive(buf, length); } int am7990_device_base::receive(u8 *buf, int length) { // check receiver enabled if (!(m_csr[0] & CSR0_RXON)) { LOGMASKED(LOG_RXTX, "receive disabled, external packet discarded\n"); return -1; } // address filter if (!address_filter(buf)) return -1; LOGMASKED(LOG_RXTX, "receive packet length %d\n", length); dump_bytes(buf, length); // check we have a buffer u32 ring_address = (m_rx_ring_base + (m_rx_ring_pos << 3)) & RING_ADDR_MASK; m_rx_md[1] = m_dma_in_cb(ring_address | 2); if (!(m_rx_md[1] & RMD1_OWN)) return -2; // flag start of packet m_rx_md[1] |= RMD1_STP; int offset = 0; while (offset < length) { // read rmd0 and rmd2 m_rx_md[0] = m_dma_in_cb(ring_address | 0); m_rx_md[2] = m_dma_in_cb(ring_address | 4); u32 const rx_buf_address = (u32(m_rx_md[1] & 0xff) << 16) | m_rx_md[0]; int const rx_buf_length = get_buf_length(m_rx_md[2]); // FIXME: In the C-LANCE device, the case of all 0's in the receive // descriptor may produce unpredictable results. // write the data to memory int const count = std::min(length - offset, rx_buf_length); dma_out(rx_buf_address, &buf[offset], count); offset += count; LOGMASKED(LOG_RXTX, "receive buffer address 0x%06x length %d wrote %d\n", rx_buf_address, rx_buf_length, count); // clear ownership m_rx_md[1] &= ~RMD1_OWN; if (offset < length) { // look ahead to next descriptor u8 const next_ring_pos = (m_rx_ring_pos + 1) & m_rx_ring_mask; u32 const next_ring_address = (m_rx_ring_base + (next_ring_pos << 3)) & RING_ADDR_MASK; // only read the next descriptor if there's more than one in the ring u16 const next_rmd1 = (next_ring_address != ring_address) ? m_dma_in_cb(next_ring_address | 2) : m_rx_md[1]; // check ownership of the next descriptor if (next_rmd1 & RMD1_OWN) { // update the descriptor m_dma_out_cb(ring_address | 2, m_rx_md[1]); // advance the ring m_rx_ring_pos = next_ring_pos; ring_address = next_ring_address; m_rx_md[1] = next_rmd1; } else { // overflow error m_rx_md[1] |= RMD1_ERR | RMD1_BUFF; break; } } } if (offset == length) { // check fcs if (!(m_mode & MODE_LOOP) || (m_mode & MODE_DTCR)) { u32 const crc = util::crc32_creator::simple(buf, length); if (~crc != FCS_RESIDUE) { LOGMASKED(LOG_RXTX, "receive incorrect fcs 0x%08x\n", ~crc); m_rx_md[1] |= RMD1_ERR | RMD1_CRC; } } m_rx_md[1] |= RMD1_ENP; } return offset; } void am7990_device_base::recv_complete_cb(int result) { switch (result) { case -2: // missed packet m_csr[0] |= CSR0_ERR | CSR0_MISS; break; case -1: // packet discarded or filtered return; default: // received something { // update the final descriptor u32 const ring_address = (m_rx_ring_base + (m_rx_ring_pos << 3)) & RING_ADDR_MASK; LOGMASKED(LOG_RXTX, "receive complete rmd1 0x%04x rmd3 %d\n", m_rx_md[1], result & RMD3_MCNT); m_dma_out_cb(ring_address | 2, m_rx_md[1]); m_dma_out_cb(ring_address | 6, result & RMD3_MCNT); // advance the ring m_rx_ring_pos = (m_rx_ring_pos + 1) & m_rx_ring_mask; } break; } // generate receive interrupt m_csr[0] |= CSR0_RINT | CSR0_INTR; update_interrupts(); } void am7990_device_base::transmit_poll(void *ptr, s32 param) { // check transmitter enabled if (m_csr[0] & CSR0_TXON) { // clear transmit demand m_csr[0] &= ~CSR0_TDMD; // read a transmit descriptor u32 const ring_address = (m_tx_ring_base + (m_tx_ring_pos << 3)) & RING_ADDR_MASK; m_tx_md[1] = m_dma_in_cb(ring_address | 2); // check ownership if (m_tx_md[1] & TMD1_OWN) { // check for start of packet if (!(m_tx_md[1] & TMD1_STP)) { // clear ownership m_dma_out_cb(ring_address | 2, m_tx_md[1] & ~TMD1_OWN); // advance the ring m_tx_ring_pos = (m_tx_ring_pos + 1) & m_tx_ring_mask; } else transmit(); } } // receive pending loopback data if (m_lb_length && (m_mode & MODE_LOOP)) { LOGMASKED(LOG_RXTX, "receive loopback packet length %d\n", m_lb_length); int const result = receive(m_lb_buf, m_lb_length); m_lb_length = 0; recv_complete_cb(result); } } void am7990_device_base::transmit() { // stop transmit polling m_transmit_poll->enable(false); // check whether to append fcs bool append_fcs = !(m_mode & MODE_DTCR); // this bit can be used to detect C-LANCE if (type() == AM79C90) append_fcs = append_fcs || bool(m_tx_md[1] & TMD1_ADD_FCS); else m_tx_md[1] &= ~TMD1_ADD_FCS; u32 ring_address = (m_tx_ring_base + (m_tx_ring_pos << 3)) & RING_ADDR_MASK; u8 buf[4096]; int length = 0; while (true) { // read tmd0 and tmd2 m_tx_md[0] = m_dma_in_cb(ring_address | 0); m_tx_md[2] = m_dma_in_cb(ring_address | 4); m_tx_md[3] = 0; u32 const tx_buf_address = (u32(m_tx_md[1] & TMD1_HADR) << 16) | m_tx_md[0]; int const tx_buf_length = get_buf_length(m_tx_md[2]); LOGMASKED(LOG_RXTX, "transmit buffer address 0x%06x length %d%s%s%s\n", tx_buf_address, tx_buf_length, m_tx_md[1] & TMD1_OWN ? " OWN" : "", m_tx_md[1] & TMD1_STP ? " STP" : "", m_tx_md[1] & TMD1_ENP ? " ENP" : ""); // clear ownership m_tx_md[1] &= ~TMD1_OWN; // FIXME: zero length transmit buffer if (tx_buf_length == 0) { // update the descriptor m_dma_out_cb(ring_address | 2, m_tx_md[1]); // advance the ring m_tx_ring_pos = (m_tx_ring_pos + 1) & m_tx_ring_mask; return; } // minimum length 100 when chaining, or 64 when not, except in loopback mode if (!length && !(m_mode & MODE_LOOP) && tx_buf_length < ((m_tx_md[1] & TMD1_ENP) ? (append_fcs ? 60 : 64) : 100)) logerror("first transmit buffer length %d less than required minimum\n", tx_buf_length); // read the data from memory dma_in(tx_buf_address, &buf[length], tx_buf_length); length += tx_buf_length; // check for end of packet if (!(m_tx_md[1] & TMD1_ENP)) { // look ahead to next descriptor u8 const next_ring_pos = (m_tx_ring_pos + 1) & m_tx_ring_mask; u32 const next_ring_address = (m_tx_ring_base + (next_ring_pos << 3)) & RING_ADDR_MASK; // only read the next descriptor if there's more than one in the ring u16 const next_tmd1 = (next_ring_address != ring_address) ? m_dma_in_cb(next_ring_address | 2) : m_tx_md[1]; if (next_tmd1 & TMD1_OWN) { // update the descriptor m_dma_out_cb(ring_address | 2, m_tx_md[1]); // advance the ring m_tx_ring_pos = next_ring_pos; ring_address = next_ring_address; m_tx_md[1] = next_tmd1; } else { // buffer error m_tx_md[1] |= TMD1_ERR; m_tx_md[3] |= TMD3_BUFF | TMD3_UFLO; m_dma_out_cb(ring_address | 6, m_tx_md[3]); // turn off the transmitter m_csr[0] &= ~CSR0_TXON; break; } } else break; } // check for babble if (length > 1518) m_csr[0] |= CSR0_ERR | CSR0_BABL; // compute and append the fcs if (append_fcs) { u32 const crc = util::crc32_creator::simple(buf, length); // insert the fcs buf[length++] = crc >> 0; buf[length++] = crc >> 8; buf[length++] = crc >> 16; buf[length++] = crc >> 24; } LOGMASKED(LOG_RXTX, "transmit sending packet length %d\n", length); dump_bytes(buf, length); // handle loopback if (m_mode & MODE_LOOP) { // forced collision if ((m_mode & MODE_COLL) && (m_mode & MODE_INTL)) { send_complete_cb(-1); return; } int const fcs_length = append_fcs ? 4 : 0; if ((length - fcs_length) < 8 || (length - fcs_length) > 32) { logerror("transmit invalid loopback packet length %d\n", length - fcs_length); // FIXME: don't know what to do, so just drop the packet send_complete_cb(-2); return; } memcpy(m_lb_buf, buf, length); m_lb_length = length; if (m_mode & MODE_INTL) { send_complete_cb(length); return; } } send(buf, length); } void am7990_device_base::send_complete_cb(int result) { u32 const ring_address = (m_tx_ring_base + (m_tx_ring_pos << 3)) & RING_ADDR_MASK; // update tmd3 on error switch (result) { case -2: // invalid loopback packet m_tx_md[1] |= TMD1_ERR; break; case -1: // forced collision m_tx_md[1] |= TMD1_ERR; m_tx_md[3] |= TMD3_RTRY; m_dma_out_cb(ring_address | 6, m_tx_md[3]); break; case 0: // failure to transmit (assume loss of carrier) m_tx_md[1] |= TMD1_ERR; m_tx_md[3] |= TMD3_LCAR; m_dma_out_cb(ring_address | 6, m_tx_md[3]); break; } LOGMASKED(LOG_RXTX, "transmit complete tmd1 0x%04x tmd3 0x%04x\n", m_tx_md[1], m_tx_md[3]); // update the last descriptor m_dma_out_cb(ring_address | 2, m_tx_md[1]); // advance the ring m_tx_ring_pos = (m_tx_ring_pos + 1) & m_tx_ring_mask; // generate transmit interrupt m_csr[0] |= CSR0_TINT | CSR0_INTR; update_interrupts(); // resume transmit polling (back-to-back) m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD); } READ16_MEMBER(am7990_device_base::regs_r) { if (!offset) { LOGMASKED(LOG_REG, "regs_r csr%d data 0x%04x (%s)\n", m_rap, m_csr[m_rap], machine().describe_context()); if (m_rap && !(m_csr[0] & CSR0_STOP)) return space.unmap(); else return m_csr[m_rap]; } else return m_rap; } WRITE16_MEMBER(am7990_device_base::regs_w) { if (!offset) { LOGMASKED(LOG_REG, "regs_w csr%d data 0x%04x (%s)\n", m_rap, data, machine().describe_context()); switch (m_rap) { case 0: // Control/Status /* * All bits are cleared by reset or STOP, except for STOP which is set. * * INIT, STRT, STOP, TDMD - read/write with 1 only * TXON, RXON, INTR, ERR - read only * INEA - read/write * IDON, TINT, RINT, MERR, MISS, CERR, BABL - read/clear only * */ // STOP takes priority over all other bits if (data & CSR0_STOP) { if (!(m_csr[0] & CSR0_STOP)) device_reset(); break; } // interrupt/error flags are all cleared by writing 1 m_csr[0] &= ~(data & (CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR | CSR0_RINT | CSR0_TINT | CSR0_IDON)); // handle INIT if ((data & CSR0_INIT) && !(m_csr[0] & CSR0_INIT)) { if (m_csr[0] & CSR0_STOP) initialize(); else m_csr[0] |= m_idon ? CSR0_IDON : CSR0_INIT; } /* * From the Am7990 datasheet: * * The STOP bit must be set prior to setting the STRT bit. INIT * and STRT must not be set at the same time. The LANCE must be * initialized first and the user must wait for the IDON bit to * be set (IDON=1) before setting the STRT bit. * * And: * * The STOP bit must be set prior to setting the INIT bit. * Setting INIT clears the STOP bit. * * This is clearly contradictory; driver code sets the STRT bit * after INIT, so assume STRT does not require STOP to be set. * * HP9000/3xx diagnostic sets INIT and STRT simultaneously. */ // handle STRT if ((data & CSR0_STRT) && !(m_csr[0] & CSR0_STRT)) { LOG("START receiver %s transmitter %s\n", (m_mode & MODE_DRX) ? "OFF" : "ON", (m_mode & MODE_DTX) ? "OFF" : "ON"); m_csr[0] |= CSR0_STRT; m_csr[0] &= ~CSR0_STOP; if (m_mode & MODE_DRX) m_csr[0] &= ~CSR0_RXON; else m_csr[0] |= CSR0_RXON; if (m_mode & MODE_DTX) m_csr[0] &= ~CSR0_TXON; else m_csr[0] |= CSR0_TXON; // trigger an immediate transmit poll m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD); } // transmit demand if ((data & CSR0_TDMD) && !(m_csr[0] & CSR0_TDMD)) { m_csr[0] |= CSR0_TDMD; m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD); } // interrupt enable if (!(m_csr[0] & CSR0_STOP) || type() == AM79C90) { // interrupt enable is read/write if ((data ^ m_csr[0]) & CSR0_INEA) LOG("interrupts %s\n", data & CSR0_INEA ? "enabled" : "disabled"); if (data & CSR0_INEA) m_csr[0] |= CSR0_INEA; else m_csr[0] &= ~CSR0_INEA; } // ERR == BABL || CERR || MISS || MERR if (m_csr[0] & CSR0_ANY_ERR) m_csr[0] |= CSR0_ERR; else m_csr[0] &= ~CSR0_ERR; // INTR == BABL || MISS || MERR || RINT || TINT || IDON if (m_csr[0] & CSR0_ANY_INTR) m_csr[0] |= CSR0_INTR; else m_csr[0] &= ~CSR0_INTR; update_interrupts(); break; case 1: // Least significant 15 bits of the Initialization Block // Datasheet says "must be zero", but doesn't indicate what // happens if it's written non-zero. Must be writable to pass // system diagnostic on MIPS RS2030. if (m_csr[0] & CSR0_STOP) m_csr[1] = data; break; case 2: // Most significant 8 bits of the Initialization Block // The C-LANCE datasheet explicitly states these bits read and // write as zero, while LANCE datasheet just says "reserved". // MIPS RS2030 diagnostic requires these bits to be writable, // so assuming this is older device behaviour. if (m_csr[0] & CSR0_STOP) m_csr[2] = (type() == AM7990) ? data : (data & 0x00ff); break; case 3: // Bus master interface if (m_csr[0] & CSR0_STOP) m_csr[3] = data & CSR3_MASK; break; } } else m_rap = data & 3; } void am7990_device_base::initialize() { u32 init_addr = ((u32(m_csr[2]) << 16) | m_csr[1]) & INIT_ADDR_MASK; u16 init_block[12]; LOG("INITIALIZE initialization block address 0x%08x\n", init_addr); for (int i = 0; i < 12; i++) init_block[i] = m_dma_in_cb(init_addr + i * 2); m_mode = init_block[0]; set_promisc(m_mode & MODE_PROM); m_physical_addr[0] = init_block[1]; m_physical_addr[1] = init_block[1] >> 8; m_physical_addr[2] = init_block[2]; m_physical_addr[3] = init_block[2] >> 8; m_physical_addr[4] = init_block[3]; m_physical_addr[5] = init_block[3] >> 8; set_mac((char *)m_physical_addr); m_logical_addr_filter = (u64(init_block[7]) << 48) | (u64(init_block[6]) << 32) | (u32(init_block[5]) << 16) | init_block[4]; m_rx_ring_base = ((u32(init_block[9]) << 16) | init_block[8]) & RING_ADDR_MASK; m_tx_ring_base = ((u32(init_block[11]) << 16) | init_block[10]) & RING_ADDR_MASK; m_rx_ring_mask = ~u8(1 << ((init_block[9] >> 13) & 7)); m_tx_ring_mask = ~u8(1 << ((init_block[11] >> 13) & 7)); m_tx_ring_pos = 0; m_rx_ring_pos = 0; LOGMASKED(LOG_INIT, "mode 0x%04x physical address %02x-%02x-%02x-%02x-%02x-%02x\n", m_mode, m_physical_addr[0], m_physical_addr[1], m_physical_addr[2], m_physical_addr[3], m_physical_addr[4], m_physical_addr[5]); LOGMASKED(LOG_INIT, "logical address filter 0x%016x\n", m_logical_addr_filter); LOGMASKED(LOG_INIT, "receive ring address 0x%08x length %d\n", m_rx_ring_base, 1 << ((init_block[9] >> 13) & 7)); LOGMASKED(LOG_INIT, "transmit ring address 0x%08x length %d\n", m_tx_ring_base, 1 << ((init_block[11] >> 13) & 7)); m_csr[0] |= CSR0_IDON | CSR0_INIT; m_csr[0] &= ~CSR0_STOP; m_idon = true; } void am7990_device_base::dma_in(u32 address, u8 *buf, int length) { // odd address start if (address & 1) { u16 const word = m_dma_in_cb(address & ~1); if (m_csr[3] & CSR3_BSWP) buf[0] = word & 0xff; else buf[0] = word >> 8; buf++; address++; length--; } // word loop while (length > 1) { u16 const word = m_dma_in_cb(address); if (m_csr[3] & CSR3_BSWP) { buf[0] = word >> 8; buf[1] = word & 0xff; } else { buf[0] = word & 0xff; buf[1] = word >> 8; } buf += 2; address += 2; length -= 2; } // trailing byte if (length) { u16 const word = m_dma_in_cb(address); if (m_csr[3] & CSR3_BSWP) buf[0] = word >> 8; else buf[0] = word & 0xff; buf++; address++; length--; } } void am7990_device_base::dma_out(u32 address, u8 *buf, int length) { // odd address start if (address & 1) { if (m_csr[3] & CSR3_BSWP) m_dma_out_cb(address & ~1, buf[0], 0x00ff); else m_dma_out_cb(address & ~1, buf[0] << 8, 0xff00); buf++; address++; length--; } // word loop while (length > 1) { u16 const word = (m_csr[3] & CSR3_BSWP) ? (buf[0] << 8) | buf[1] : (buf[1] << 8) | buf[0]; m_dma_out_cb(address, word); buf += 2; address += 2; length -= 2; } // trailing byte if (length) { if (m_csr[3] & CSR3_BSWP) m_dma_out_cb(address, buf[0] << 8, 0xff00); else m_dma_out_cb(address, buf[0], 0x00ff); buf++; address++; length--; } } void am7990_device_base::dump_bytes(u8 *buf, int length) { if (VERBOSE & LOG_PACKETS) { // pad with zeros to 8-byte boundary for (int i = 0; i < 8 - (length % 8); i++) buf[length + i] = 0; // dump length / 8 (rounded up) groups of 8 bytes for (int i = 0; i < (length + 7) / 8; i++) LOGMASKED(LOG_PACKETS, "%02x %02x %02x %02x %02x %02x %02x %02x\n", buf[i * 8 + 0], buf[i * 8 + 1], buf[i * 8 + 2], buf[i * 8 + 3], buf[i * 8 + 4], buf[i * 8 + 5], buf[i * 8 + 6], buf[i * 8 + 7]); } } bool am7990_device_base::address_filter(u8 *buf) { if (m_mode & MODE_PROM) { LOGMASKED(LOG_FILTER, "address_filter accepted (promiscuous mode)\n"); return true; } if (buf[0] & 1) { // broadcast if (!memcmp(ETH_BROADCAST, buf, 6)) { LOGMASKED(LOG_FILTER, "address_filter accepted (broadcast) %02x-%02x-%02x-%02x-%02x-%02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); return true; } // multicast /* * Multicast address matching is performed by computing the fcs crc of * the destination address, and then using the upper 6 bits as an index * into the 64-bit logical address filter. */ u32 const crc = util::crc32_creator::simple(buf, 6); if (BIT(m_logical_addr_filter, 63 - (crc >> 26))) { LOGMASKED(LOG_FILTER, "address_filter accepted (logical address match) %02x-%02x-%02x-%02x-%02x-%02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); return true; } } else // unicast if (!memcmp(m_physical_addr, buf, 6)) { LOGMASKED(LOG_FILTER, "address_filter accepted (physical address match)\n"); return true; } return false; }