Source to src/ethernet.c
/* Previous - ethernet.c
This file is distributed under the GNU Public License, version 2 or at
your option any later version. Read the file gpl.txt for details.
Network adapter for non-turbo and turbo NeXT machines.
*/
#include "ioMem.h"
#include "ioMemTables.h"
#include "m68000.h"
#include "configuration.h"
#include "sysReg.h"
#include "dma.h"
#include "bmap.h"
#include "ethernet.h"
#include "enet_slirp.h"
#include "enet_pcap.h"
#include "cycInt.h"
#include "statusbar.h"
#define LOG_EN_LEVEL LOG_DEBUG
#define LOG_EN_REG_LEVEL LOG_DEBUG
#define LOG_EN_DATA 0
#define IO_SEG_MASK 0x1FFFF
struct {
Uint8 tx_status;
Uint8 tx_mask;
Uint8 tx_mode;
Uint8 rx_status;
Uint8 rx_mask;
Uint8 rx_mode;
Uint8 reset;
Uint8 mac_addr[6];
} enet;
bool enet_stopped;
#define TXSTAT_READY 0x80 /* r */
#define TXSTAT_NET_BUSY 0x40 /* r */
#define TXSTAT_TX_RECVD 0x20 /* r */
#define TXSTAT_SHORTED 0x10 /* r */
#define TXSTAT_UNDERFLOW 0x08 /* rw */
#define TXSTAT_COLL 0x04 /* rw */
#define TXSTAT_16COLLS 0x02 /* rw */
#define TXSTAT_PAR_ERR 0x01 /* rw */
#define TXMASK_PKT_RDY 0x80
#define TXMASK_TX_RECVD 0x20
#define TXMASK_UNDERFLOW 0x08
#define TXMASK_COLL 0x04
#define TXMASK_16COLLS 0x02
#define TXMASK_PAR_ERR 0x01
#define RXSTAT_PKT_OK 0x80 /* rw */
#define RXSTAT_RESET_PKT 0x10 /* r */
#define RXSTAT_SHORT_PKT 0x08 /* rw */
#define RXSTAT_ALIGN_ERR 0x04 /* rw */
#define RXSTAT_CRC_ERR 0x02 /* rw */
#define RXSTAT_OVERFLOW 0x01 /* rw */
#define RXMASK_PKT_OK 0x80
#define RXMASK_RESET_PKT 0x10
#define RXMASK_SHORT_PKT 0x80
#define RXMASK_ALIGN_ERR 0x40
#define RXMASK_CRC_ERR 0x20
#define RXMASK_OVERFLOW 0x10
#define TXMODE_COLL_ATMPT 0xF0 /* r */
#define TXMODE_IGNORE_PAR 0x08 /* rw */
#define TXMODE_TM 0x04 /* rw */
#define TXMODE_DIS_LOOP 0x02 /* rw */
#define TXMODE_DIS_CONTNT 0x01 /* rw */
#define RXMODE_TEST_CRC 0x80
#define RXMODE_ADDR_SIZE 0x10
#define RXMODE_ENA_SHORT 0x08
#define RXMODE_ENA_RST 0x04
#define RXMODE_MATCH_MODE 0x03
#define EN_RESET 0x80 /* w */
void enet_reset(void);
void (*enet_output)(void);
void (*enet_input)(Uint8 *pkt, int pkt_len);
void (*enet_start)(Uint8 *mac);
void (*enet_stop)(void);
void EN_TX_Status_Read(void) { // 0x02006000
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.tx_status;
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Transmitter status read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_TX_Status_Write(void) {
Uint8 val=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Transmitter status write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
if (ConfigureParams.System.bTurbo) {
enet.tx_status&=~val;
} else {
enet.tx_status&=~(val&0x0F);
}
if ((enet.tx_status&enet.tx_mask&0x0F)==0) {
set_interrupt(INT_EN_TX, RELEASE_INT);
}
}
void EN_TX_Mask_Read(void) { // 0x02006001
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.tx_mask&0xAF;
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Transmitter masks read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_TX_Mask_Write(void) {
enet.tx_mask=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Transmitter masks write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
if ((enet.tx_status&enet.tx_mask&0x0F)==0) {
set_interrupt(INT_EN_TX, RELEASE_INT);
}
}
void EN_RX_Status_Read(void) { // 0x02006002
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.rx_status;
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver status read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_RX_Status_Write(void) {
Uint8 val=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver status write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
enet.rx_status&=~(val&0x8F);
if ((enet.rx_status&enet.rx_mask&0x8F)==0) {
set_interrupt(INT_EN_RX, RELEASE_INT);
}
}
void EN_RX_Mask_Read(void) { // 0x02006003
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.rx_mask&0x9F;
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver masks read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_RX_Mask_Write(void) {
enet.rx_mask=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver masks write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
if ((enet.rx_status&enet.rx_mask&0x8F)==0) {
set_interrupt(INT_EN_RX, RELEASE_INT);
}
}
void EN_TX_Mode_Read(void) { // 0x02006004
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.tx_mode;
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Transmitter mode read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_TX_Mode_Write(void) {
enet.tx_mode=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Transmitter mode write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_RX_Mode_Read(void) { // 0x02006005
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.rx_mode;
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver mode read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_RX_Mode_Write(void) {
enet.rx_mode=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver mode write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_Reset_Write(void) { // 0x02006006
enet.reset=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Reset write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
enet_reset();
}
void EN_NodeID0_Read(void) { // 0x02006008
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.mac_addr[0];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 0 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID0_Write(void) {
enet.mac_addr[0]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 0 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID1_Read(void) { // 0x02006009
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.mac_addr[1];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 1 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID1_Write(void) {
enet.mac_addr[1]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 1 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID2_Read(void) { // 0x0200600a
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.mac_addr[2];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 2 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID2_Write(void) {
enet.mac_addr[2]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 2 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID3_Read(void) { // 0x0200600b
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.mac_addr[3];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 3 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID3_Write(void) {
enet.mac_addr[3]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 3 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID4_Read(void) { // 0x0200600c
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.mac_addr[4];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 4 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID4_Write(void) {
enet.mac_addr[4]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 4 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID5_Read(void) { // 0x0200600d
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = enet.mac_addr[5];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 5 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_NodeID5_Write(void) {
enet.mac_addr[5]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
Log_Printf(LOG_EN_REG_LEVEL,"[EN] MAC byte 5 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_CounterLo_Read(void) { // 0x02006007
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = ((enet_tx_buffer.limit-enet_tx_buffer.size)*8)&0xFF; /* FIXME: counter value */
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver mode read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_CounterHi_Read(void) { // 0x0200600f
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = (((enet_tx_buffer.limit-enet_tx_buffer.size)*8)>>8)&0x3F; /* FIXME: counter value */
Log_Printf(LOG_EN_REG_LEVEL,"[EN] Receiver mode read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
static void enet_tx_interrupt(Uint8 intr) {
enet.tx_status|=intr;
if (enet.tx_status&enet.tx_mask) {
set_interrupt(INT_EN_TX, SET_INT);
}
}
static void enet_rx_interrupt(Uint8 intr) {
enet.rx_status|=intr;
if (enet.rx_status&enet.rx_mask) {
set_interrupt(INT_EN_RX, SET_INT);
}
}
/* Functions to find out if we are intended to receive a packet */
/* Non-turbo */
#define RX_NOPACKETS 0 // Accept no packets
#define RX_LIMITED 1 // Accept broadcast/limited
#define RX_NORMAL 2 // Accept broadcast/multicast
#define RX_PROMISCUOUS 3 // Accept all packets
/* Turbo */
#define RX_ENABLED 0x80 // Accept packets
#define RX_ANY 0x01 // Accept any packets
#define RX_OWN 0x02 // Accept own packets
static bool recv_multicast(Uint8 *packet) {
if (packet[0]&0x01)
return true;
else
return false;
}
static bool recv_local_multicast(Uint8 *packet) {
if (packet[0]&0x01 &&
(packet[0]&0xFE) == enet.mac_addr[0] &&
packet[1] == enet.mac_addr[1] &&
packet[2] == enet.mac_addr[2])
return true;
else
return false;
}
static bool recv_me(Uint8 *packet) {
if (packet[0] == enet.mac_addr[0] &&
packet[1] == enet.mac_addr[1] &&
packet[2] == enet.mac_addr[2] &&
packet[3] == enet.mac_addr[3] &&
packet[4] == enet.mac_addr[4] &&
(packet[5] == enet.mac_addr[5] || (enet.rx_mode&RXMODE_ADDR_SIZE)))
return true;
else
return false;
}
static bool recv_me_turbo(Uint8 *packet) {
if (packet[0] == enet.mac_addr[0] &&
packet[1] == enet.mac_addr[1] &&
packet[2] == enet.mac_addr[2] &&
packet[3] == enet.mac_addr[3] &&
packet[4] == enet.mac_addr[4] &&
packet[5] == enet.mac_addr[5])
return true;
else
return false;
}
static bool recv_broadcast(Uint8 *packet) {
if (packet[0] == 0xFF &&
packet[1] == 0xFF &&
packet[2] == 0xFF &&
packet[3] == 0xFF &&
packet[4] == 0xFF &&
packet[5] == 0xFF)
return true;
else
return false;
}
static bool enet_packet_for_me(Uint8 *packet) {
if (ConfigureParams.System.bTurbo) {
if (enet.rx_mode&RX_ENABLED) {
if (enet.rx_mode&RX_ANY) {
return true;
} else if (enet.rx_mode&RX_OWN) {
if (recv_broadcast(packet) || recv_me_turbo(packet)) {
return true;
}
} else {
if (recv_broadcast(packet)) {
return true;
}
}
}
return false;
}
switch (enet.rx_mode&RXMODE_MATCH_MODE) {
case RX_NOPACKETS:
return false;
case RX_LIMITED:
if (recv_broadcast(packet) || recv_me(packet) || recv_local_multicast(packet))
return true;
else
return false;
case RX_NORMAL:
if (recv_broadcast(packet) || recv_me(packet) || recv_multicast(packet))
return true;
else
return false;
case RX_PROMISCUOUS:
return true;
default: return false;
}
}
void enet_receive(Uint8 *pkt, int len) {
if (enet_packet_for_me(pkt)) {
#if 1 /* Hack for short packets from SLIRP */
if (len<60) {
Log_Printf(LOG_WARN, "[EN] HACK: short packet received (%i byte). Fixed.", len);
len = 60;
}
#endif
memcpy(enet_rx_buffer.data,pkt,len);
enet_rx_buffer.size=enet_rx_buffer.limit=len;
enet.tx_status |= TXSTAT_NET_BUSY;
} else {
Log_Printf(LOG_WARN, "[EN] Packet is not for me.");
}
}
static void print_buf(Uint8 *buf, Uint32 size) {
#if LOG_EN_DATA
int i;
for (i=0; i<size; i++) {
if (i==14 || (i-14)%16==0) {
printf("\n");
}
printf("%02X ",buf[i]);
}
printf("\n");
#endif
}
#define ENET_FRAMESIZE_MIN 64 /* 46 byte data and 14 byte header, 4 byte CRC */
#define ENET_FRAMESIZE_MAX 1518 /* 1500 byte data and 14 byte header, 4 byte CRC */
/* Ethernet periodic check */
#define ENET_IO_DELAY 500 /* use 500 for NeXT hardware test, 20 for status test */
#define ENET_IO_SHORT 40 /* use 40 for 68030 hardware test */
enum {
RECV_STATE_WAITING,
RECV_STATE_RECEIVING
} receiver_state;
bool tx_done;
bool rx_chain;
int old_size;
int en_state;
#define EN_DISCONNECTED 0
#define EN_LOOPBACK 1
#define EN_THINWIRE 2
#define EN_TWISTEDPAIR 3
/* Fujitsu ethernet controller */
static int enet_state(void) {
if (ConfigureParams.System.nMachineType == NEXT_CUBE030) {
if (enet.tx_mode&TXMODE_DIS_LOOP) {
if (ConfigureParams.Ethernet.bEthernetConnected) {
return EN_THINWIRE;
}
} else {
return EN_LOOPBACK;
}
} else if (bmap_tpe_select) {
if (ConfigureParams.Ethernet.bEthernetConnected) {
if (ConfigureParams.Ethernet.bTwistedPair) {
return EN_TWISTEDPAIR;
}
}
} else {
if (enet.tx_mode&TXMODE_DIS_LOOP) {
if (ConfigureParams.Ethernet.bEthernetConnected) {
if (!ConfigureParams.Ethernet.bTwistedPair) {
return EN_THINWIRE;
}
}
} else {
return EN_LOOPBACK;
}
}
return EN_DISCONNECTED;
}
static void enet_io(void) {
en_state = enet_state();
/* Receive packet */
switch (receiver_state) {
case RECV_STATE_WAITING:
if (enet_rx_buffer.size>0) {
Statusbar_BlinkLed(DEVICE_LED_ENET);
Log_Printf(LOG_EN_LEVEL, "[EN] Receiving packet from %02X:%02X:%02X:%02X:%02X:%02X",
enet_rx_buffer.data[6], enet_rx_buffer.data[7], enet_rx_buffer.data[8],
enet_rx_buffer.data[9], enet_rx_buffer.data[10], enet_rx_buffer.data[11]);
print_buf(enet_rx_buffer.data, enet_rx_buffer.size);
enet_rx_buffer.size+=4;
enet_rx_buffer.limit+=4;
rx_chain = false;
enet.rx_status&=~RXSTAT_PKT_OK;
if (enet_rx_buffer.size<ENET_FRAMESIZE_MIN && !(enet.rx_mode&RXMODE_ENA_SHORT)) {
Log_Printf(LOG_WARN, "[EN] Received packet is short (%i byte)",enet_rx_buffer.size);
enet_rx_interrupt(RXSTAT_SHORT_PKT);
enet_rx_buffer.size = 0;
enet.tx_status &= ~TXSTAT_NET_BUSY;
break; /* Keep on waiting for a good packet */
} else /* Fall through to receiving state */
receiver_state = RECV_STATE_RECEIVING;
} else if (en_state == EN_THINWIRE || en_state == EN_TWISTEDPAIR) {
/* Receive from real world network */
enet_output();
break;
} else
break;
case RECV_STATE_RECEIVING:
if (enet_rx_buffer.size>0) {
old_size = enet_rx_buffer.size;
dma_enet_write_memory(rx_chain);
if (enet_rx_buffer.size==old_size) {
Log_Printf(LOG_WARN, "[EN] Receiving packet: Error! Receiver overflow (DMA disabled)!");
enet_rx_interrupt(RXSTAT_OVERFLOW);
rx_chain = false;
enet_rx_buffer.size = 0;
enet.tx_status &= ~TXSTAT_NET_BUSY;
receiver_state = RECV_STATE_WAITING;
break; /* Go back to waiting state */
}
if (enet_rx_buffer.size>0) {
Log_Printf(LOG_WARN, "[EN] Receiving packet: Transfer not complete!");
rx_chain = true;
break; /* Loop in receiving state */
} else { /* done */
Log_Printf(LOG_EN_LEVEL, "[EN] Receiving packet: Transfer complete.");
rx_chain = false;
enet_rx_interrupt(RXSTAT_PKT_OK);
if (en_state == EN_LOOPBACK) { /* same for thin wire loopback? */
enet_tx_interrupt(TXSTAT_TX_RECVD);
}
enet.tx_status &= ~TXSTAT_NET_BUSY;
receiver_state = RECV_STATE_WAITING;
}
}
break;
default:
break;
}
/* Send packet */
if (enet.tx_status&TXSTAT_READY) {
if (en_state != EN_DISCONNECTED) {
if (enet.tx_status&TXSTAT_NET_BUSY) {
/* Wait until network is free */
Log_Printf(LOG_WARN, "[EN] Network is busy. Transmission delayed.");
} else {
old_size = enet_tx_buffer.size;
tx_done=dma_enet_read_memory();
if (enet_tx_buffer.size>0) {
enet.tx_status &= ~TXSTAT_TX_RECVD;
if (enet_tx_buffer.size==old_size && !tx_done) {
Log_Printf(LOG_WARN, "[EN] Sending packet: Error! Transmitter underflow (no EOP)!");
enet_tx_interrupt(TXSTAT_UNDERFLOW);
enet_tx_buffer.size=0;
} else if (enet_tx_buffer.size>15) {
enet_tx_buffer.size-=15;
} else if (tx_done) {
Log_Printf(LOG_WARN, "[EN] Transmitter error: Early EOP!");
enet_tx_buffer.size=0;
tx_done = false;
}
}
if (tx_done) {
Statusbar_BlinkLed(DEVICE_LED_ENET);
Log_Printf(LOG_EN_LEVEL, "[EN] Sending packet to %02X:%02X:%02X:%02X:%02X:%02X",
enet_tx_buffer.data[0], enet_tx_buffer.data[1], enet_tx_buffer.data[2],
enet_tx_buffer.data[3], enet_tx_buffer.data[4], enet_tx_buffer.data[5]);
print_buf(enet_tx_buffer.data, enet_tx_buffer.size);
if (en_state == EN_LOOPBACK) {
/* Loop back */
Log_Printf(LOG_WARN, "[EN] Loopback packet.");
enet_receive(enet_tx_buffer.data, enet_tx_buffer.size);
} else {
/* Send to real world network */
enet_input(enet_tx_buffer.data,enet_tx_buffer.size);
/* Simultaneously receive packet on thin ethernet */
if (en_state == EN_THINWIRE) {
enet_receive(enet_tx_buffer.data, enet_tx_buffer.size);
}
}
enet_tx_buffer.size=0;
}
}
}
}
}
/* AT&T ethernet controller for turbo systems */
#define TXMODE_ENABLE 0x80
#define RXMODE_ENABLE 0x80
#define TXMODE_LOOP 0x02
#define TXMODE_TPE 0x04
#define ENCTRL_TPE 0x40
void EN_Control_Read(void) { // 0x02006006
Uint8 val = enet.reset;
if (ConfigureParams.Ethernet.bEthernetConnected && ConfigureParams.Ethernet.bTwistedPair) {
val &= ~ENCTRL_TPE;
} else {
val |= ENCTRL_TPE;
}
IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = val;
Log_Printf(LOG_EN_REG_LEVEL,"[newEN] Control read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
}
void EN_Control_Write(void) {
enet.reset=(IoMem[IoAccessCurrentAddress & IO_SEG_MASK])&EN_RESET;
Log_Printf(LOG_EN_REG_LEVEL,"[newEN] Control write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
enet_reset();
}
static int new_enet_state(void) {
if (enet.tx_mode&TXMODE_LOOP) {
return EN_LOOPBACK;
} else if (ConfigureParams.Ethernet.bEthernetConnected) {
if (enet.tx_mode&TXMODE_TPE) {
if (ConfigureParams.Ethernet.bTwistedPair) {
return EN_TWISTEDPAIR;
}
} else {
if (!ConfigureParams.Ethernet.bTwistedPair) {
return EN_THINWIRE;
}
}
}
return EN_DISCONNECTED;
}
static void new_enet_io(void) {
en_state = new_enet_state();
/* Receive packet */
switch (receiver_state) {
case RECV_STATE_WAITING:
if (enet_rx_buffer.size>0) {
Statusbar_BlinkLed(DEVICE_LED_ENET);
Log_Printf(LOG_EN_LEVEL, "[newEN] Receiving packet from %02X:%02X:%02X:%02X:%02X:%02X",
enet_rx_buffer.data[6], enet_rx_buffer.data[7], enet_rx_buffer.data[8],
enet_rx_buffer.data[9], enet_rx_buffer.data[10], enet_rx_buffer.data[11]);
print_buf(enet_rx_buffer.data, enet_rx_buffer.size);
enet_rx_buffer.size+=4;
enet_rx_buffer.limit+=4;
rx_chain = false;
enet.rx_status&=~RXSTAT_PKT_OK;
if (enet_rx_buffer.size<ENET_FRAMESIZE_MIN && !(enet.rx_mode&RXMODE_ENA_SHORT)) {
Log_Printf(LOG_WARN, "[newEN] Received packet is short (%i byte)",enet_rx_buffer.size);
enet_rx_interrupt(RXSTAT_SHORT_PKT);
enet_rx_buffer.size = 0;
enet.tx_status &= ~TXSTAT_NET_BUSY;
break; /* Keep on waiting for a good packet */
} else /* Fall through to receiving state */
receiver_state = RECV_STATE_RECEIVING;
} else if (en_state == EN_THINWIRE || en_state == EN_TWISTEDPAIR) {
/* Receive from real world network */
enet_output();
break;
} else
break;
case RECV_STATE_RECEIVING:
if (enet_rx_buffer.size>0) {
old_size = enet_rx_buffer.size;
dma_enet_write_memory(rx_chain);
if (enet_rx_buffer.size==old_size) {
Log_Printf(LOG_WARN, "[newEN] Receiving packet: Error! Receiver overflow (DMA disabled)!");
enet_rx_interrupt(RXSTAT_OVERFLOW);
rx_chain = false;
enet_rx_buffer.size = 0;
enet.tx_status &= ~TXSTAT_NET_BUSY;
receiver_state = RECV_STATE_WAITING;
break; /* Go back to waiting state */
}
if (enet_rx_buffer.size>0) {
Log_Printf(LOG_WARN, "[newEN] Receiving packet: Transfer not complete!");
rx_chain = true;
break; /* Loop in receiving state */
} else { /* done */
Log_Printf(LOG_EN_LEVEL, "[newEN] Receiving packet: Transfer complete.");
rx_chain = false;
enet_rx_interrupt(RXSTAT_PKT_OK);
if (en_state == EN_LOOPBACK) {
enet_tx_interrupt(TXSTAT_TX_RECVD);
}
enet.tx_status &= ~TXSTAT_NET_BUSY;
receiver_state = RECV_STATE_WAITING;
}
}
break;
default:
break;
}
/* Send packet */
if (enet.tx_mode&TXMODE_ENABLE) {
if (en_state != EN_DISCONNECTED) {
if (enet.tx_status&TXSTAT_NET_BUSY) {
/* Wait until network is free */
Log_Printf(LOG_WARN, "[EN] Network is busy. Transmission delayed.");
} else {
dma_enet_read_memory();
if (enet_tx_buffer.size>0) {
Statusbar_BlinkLed(DEVICE_LED_ENET);
Log_Printf(LOG_EN_LEVEL, "[newEN] Sending packet to %02X:%02X:%02X:%02X:%02X:%02X",
enet_tx_buffer.data[0], enet_tx_buffer.data[1], enet_tx_buffer.data[2],
enet_tx_buffer.data[3], enet_tx_buffer.data[4], enet_tx_buffer.data[5]);
print_buf(enet_tx_buffer.data, enet_tx_buffer.size);
enet.tx_status &= ~TXSTAT_TX_RECVD;
if (en_state == EN_LOOPBACK) {
/* Loop back */
Log_Printf(LOG_WARN, "[newEN] Loopback packet.");
enet_receive(enet_tx_buffer.data, enet_tx_buffer.size);
} else {
/* Send to real world network */
enet_input(enet_tx_buffer.data,enet_tx_buffer.size);
/* Simultaneously receive packet on thin ethernet */
if (en_state == EN_THINWIRE) {
enet_receive(enet_tx_buffer.data, enet_tx_buffer.size);
}
}
enet_tx_buffer.size=0;
enet_tx_interrupt(TXSTAT_READY);
}
}
} else { /* disconnected - strange, but required by ROM and 2.2 kernel */
if (ConfigureParams.Ethernet.bEthernetConnected) {
if (!ConfigureParams.Ethernet.bTwistedPair) {
enet_tx_interrupt(TXSTAT_READY);
}
}
}
enet.tx_status |= TXSTAT_READY; /* really? */
}
}
void ENET_IO_Handler(void) {
CycInt_AcknowledgeInterrupt();
if (enet.reset&EN_RESET) {
Log_Printf(LOG_WARN, "Stopping Ethernet Transmitter/Receiver");
enet_stopped=true;
/* Stop SLIRP/PCAP */
if (ConfigureParams.Ethernet.bEthernetConnected) {
enet_stop();
}
return;
}
if (ConfigureParams.System.bTurbo) {
new_enet_io();
} else {
enet_io();
}
CycInt_AddRelativeInterruptUs(receiver_state==RECV_STATE_WAITING?ENET_IO_DELAY:ENET_IO_SHORT, 0, INTERRUPT_ENET_IO);
}
void enet_reset(void) {
if (enet.reset&EN_RESET) {
enet.tx_status=ConfigureParams.System.bTurbo?0:TXSTAT_READY;
} else if (enet_stopped==true) {
Log_Printf(LOG_WARN, "Starting Ethernet Transmitter/Receiver");
enet_stopped=false;
CycInt_AddRelativeInterruptUs(ENET_IO_DELAY, 0, INTERRUPT_ENET_IO);
/* Start SLIRP/PCAP */
if (ConfigureParams.Ethernet.bEthernetConnected) {
enet_start(enet.mac_addr);
}
}
}
void Ethernet_Reset(bool hard) {
static int init_done = 0;
if (hard) {
enet.reset=EN_RESET;
enet_stopped=true;
enet_rx_buffer.size=enet_tx_buffer.size=0;
enet_rx_buffer.limit=enet_tx_buffer.limit=64*1024;
enet.tx_status=ConfigureParams.System.bTurbo?0:TXSTAT_READY;
}
if (init_done) {
/* Stop SLIRP/PCAP */
enet_stop();
}
#if HAVE_PCAP
if (ConfigureParams.Ethernet.nHostInterface == ENET_PCAP) {
enet_output = enet_pcap_queue_poll;
enet_input = enet_pcap_input;
enet_start = enet_pcap_start;
enet_stop = enet_pcap_stop;
} else
#endif
{
enet_output = enet_slirp_queue_poll;
enet_input = enet_slirp_input;
enet_start = enet_slirp_start;
enet_stop = enet_slirp_stop;
}
init_done = 1;
if (ConfigureParams.Ethernet.bEthernetConnected && !(enet.reset&EN_RESET)) {
/* Start SLIRP/PCAP */
enet_start(enet.mac_addr);
} else {
/* Stop SLIRP/PCAP */
enet_stop();
}
}