Merge pull request #708 from 86Box/bugfix/pcnet_linkstate
Set Link State from VBox to 86Box implemented on the PCnet card (any …
This commit is contained in:
@@ -66,6 +66,7 @@ enum {
|
||||
|
||||
typedef void (*NETRXCB)(void *, uint8_t *, int);
|
||||
typedef int (*NETWAITCB)(void *);
|
||||
typedef int (*NETSETLINKSTATE)(void *);
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -76,6 +77,7 @@ typedef struct {
|
||||
int (*poll)(void *);
|
||||
NETRXCB rx;
|
||||
NETWAITCB wait;
|
||||
NETSETLINKSTATE set_link_state;
|
||||
} netcard_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -101,7 +103,7 @@ extern void network_busy(uint8_t set);
|
||||
extern void network_end(void);
|
||||
|
||||
extern void network_init(void);
|
||||
extern void network_attach(void *, uint8_t *, NETRXCB, NETWAITCB);
|
||||
extern void network_attach(void *, uint8_t *, NETRXCB, NETWAITCB, NETSETLINKSTATE);
|
||||
extern void network_close(void);
|
||||
extern void network_reset(void);
|
||||
extern int network_available(void);
|
||||
|
@@ -617,7 +617,7 @@ threec503_nic_init(const device_t *info)
|
||||
dev->regs.gacfr = 0x09; /* Start with RAM mapping enabled. */
|
||||
|
||||
/* Attach ourselves to the network module. */
|
||||
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL);
|
||||
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL);
|
||||
|
||||
return(dev);
|
||||
}
|
||||
|
@@ -1469,7 +1469,7 @@ nic_init(const device_t *info)
|
||||
nic_reset(dev);
|
||||
|
||||
/* Attach ourselves to the network module. */
|
||||
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL);
|
||||
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL);
|
||||
|
||||
nelog(1, "%s: %s attached IO=0x%X IRQ=%d\n", dev->name,
|
||||
dev->is_pci?"PCI":"ISA", dev->base_address, dev->base_irq);
|
||||
|
@@ -184,7 +184,7 @@ poll_thread(void *arg)
|
||||
if (pcap == NULL) break;
|
||||
|
||||
/* Wait for the next packet to arrive. */
|
||||
if (network_get_wait() || (poll_card->wait && poll_card->wait(poll_card->priv)))
|
||||
if (network_get_wait() || (poll_card->set_link_state && poll_card->set_link_state(poll_card->priv)) || (poll_card->wait && poll_card->wait(poll_card->priv)))
|
||||
data = NULL;
|
||||
else
|
||||
data = (uint8_t *)f_pcap_next((void *)pcap, &h);
|
||||
|
@@ -64,10 +64,12 @@ typedef struct RTNETETHERHDR
|
||||
#define MII_MAX_REG 32
|
||||
#define CSR_MAX_REG 128
|
||||
|
||||
/** Maximum number of times we report a link down to the guest (failure to send frame) */
|
||||
#define PCNET_MAX_LINKDOWN_REPORTED 3
|
||||
|
||||
/** Maximum frame size we handle */
|
||||
#define MAX_FRAME 1536
|
||||
|
||||
|
||||
/** @name Bus configuration registers
|
||||
* @{ */
|
||||
#define BCR_MSRDA 0
|
||||
@@ -230,9 +232,16 @@ typedef struct {
|
||||
/** Error counter for bad receive descriptors. */
|
||||
uint32_t uCntBadRMD;
|
||||
uint16_t u16CSR0LastSeenByGuest;
|
||||
uint64_t last_poll;
|
||||
/** If set the link is currently up. */
|
||||
int fLinkUp;
|
||||
/** If set the link is temporarily down because of a saved state load. */
|
||||
int fLinkTempDown;
|
||||
/** Number of times we've reported the link down. */
|
||||
uint32_t cLinkDownReported;
|
||||
/** MS to wait before we enable the link. */
|
||||
uint32_t cMsLinkUpDelay;
|
||||
uint8_t maclocal[6]; /* configured MAC (local) address */
|
||||
pc_timer_t poll_timer, timer_soft_int;
|
||||
pc_timer_t timer_soft_int, timer_restore;
|
||||
} nic_t;
|
||||
|
||||
/** @todo All structs: big endian? */
|
||||
@@ -403,6 +412,17 @@ pcnet_do_irq(nic_t *dev, int issue)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the link is up.
|
||||
* @returns true if the link is up.
|
||||
* @returns false if the link is down.
|
||||
*/
|
||||
static __inline int
|
||||
pcnetIsLinkUp(nic_t *dev)
|
||||
{
|
||||
return !dev->fLinkTempDown && dev->fLinkUp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load transmit message descriptor
|
||||
* Make sure we read the own flag first.
|
||||
@@ -1215,6 +1235,12 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
||||
size = 60;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop packets if the cable is not connected
|
||||
*/
|
||||
if (!pcnetIsLinkUp(dev))
|
||||
return;
|
||||
|
||||
pcnetlog(1, "%s: pcnetReceiveNoSync: RX %x:%x:%x:%x:%x:%x > %x:%x:%x:%x:%x:%x len %d\n", dev->name,
|
||||
buf[6], buf[7], buf[8], buf[9], buf[10], buf[11],
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
|
||||
@@ -1408,6 +1434,28 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
||||
pcnetUpdateIrq(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails a TMD with a link down error.
|
||||
*/
|
||||
static void
|
||||
pcnetXmitFailTMDLinkDown(nic_t *dev, TMD *pTmd)
|
||||
{
|
||||
/* make carrier error - hope this is correct. */
|
||||
dev->cLinkDownReported++;
|
||||
pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
|
||||
dev->aCSR[0] |= 0x8000 | 0x2000; /* ERR | CERR */
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails a TMD with a generic error.
|
||||
*/
|
||||
static void
|
||||
pcnetXmitFailTMDGeneric(nic_t *dev, TMD *pTmd)
|
||||
{
|
||||
/* make carrier error - hope this is correct. */
|
||||
pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
|
||||
dev->aCSR[0] |= 0x8000 | 0x2000; /* ERR | CERR */
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually try transmit frames.
|
||||
@@ -1435,6 +1483,12 @@ pcnetAsyncTransmit(nic_t *dev)
|
||||
if (!pcnetTdtePoll(dev, &tmd))
|
||||
break;
|
||||
|
||||
/* Don't continue sending packets when the link is down. */
|
||||
if ((!pcnetIsLinkUp(dev)
|
||||
&& dev->cLinkDownReported > PCNET_MAX_LINKDOWN_REPORTED)
|
||||
)
|
||||
break;
|
||||
|
||||
pcnetlog(3, "%s: TMDLOAD %#010x\n", dev->name, PHYSADDR(dev, CSR_CXDA(dev)));
|
||||
|
||||
int fLoopback = CSR_LOOP(dev);
|
||||
@@ -1446,6 +1500,8 @@ pcnetAsyncTransmit(nic_t *dev)
|
||||
const int cb = 4096 - tmd.tmd1.bcnt;
|
||||
pcnetlog("%s: pcnetAsyncTransmit: stp&enp: cb=%d xmtrc=%#x\n", dev->name, cb, CSR_XMTRC(dev));
|
||||
|
||||
if ((pcnetIsLinkUp(dev) || fLoopback)) {
|
||||
|
||||
/* From the manual: ``A zero length buffer is acceptable as
|
||||
* long as it is not the last buffer in a chain (STP = 0 and
|
||||
* ENP = 1).'' That means that the first buffer might have a
|
||||
@@ -1492,6 +1548,11 @@ pcnetAsyncTransmit(nic_t *dev)
|
||||
pcnetlog(1, "%s: pcnetAsyncTransmit: illegal 4kb frame -> ignoring\n", dev->name);
|
||||
pcnetTmdStorePassHost(dev, &tmd, PHYSADDR(dev, CSR_CXDA(dev)));
|
||||
break;
|
||||
} else {
|
||||
pcnetXmitFailTMDGeneric(dev, &tmd);
|
||||
}
|
||||
} else {
|
||||
pcnetXmitFailTMDLinkDown(dev, &tmd);
|
||||
}
|
||||
|
||||
/* Write back the TMD and pass it to the host (clear own bit). */
|
||||
@@ -2021,8 +2082,9 @@ pcnet_mii_readw(nic_t *dev, uint16_t miiaddr)
|
||||
| 0x0008 /* Able to do auto-negotiation. */
|
||||
| 0x0004 /* Link up. */
|
||||
| 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
|
||||
if (isolate) {
|
||||
if (!dev->fLinkUp || dev->fLinkTempDown || isolate) {
|
||||
val &= ~(0x0020 | 0x0004);
|
||||
dev->cLinkDownReported++;
|
||||
}
|
||||
if (!autoneg) {
|
||||
/* Auto-negotiation disabled. */
|
||||
@@ -2056,7 +2118,7 @@ pcnet_mii_readw(nic_t *dev, uint16_t miiaddr)
|
||||
|
||||
case 5:
|
||||
/* Link partner ability register. */
|
||||
if (!isolate) {
|
||||
if (dev->fLinkUp && !dev->fLinkTempDown && !isolate) {
|
||||
val = 0x8000 /* Next page bit. */
|
||||
| 0x4000 /* Link partner acked us. */
|
||||
| 0x0400 /* Can do flow control. */
|
||||
@@ -2064,23 +2126,25 @@ pcnet_mii_readw(nic_t *dev, uint16_t miiaddr)
|
||||
| 0x0001; /* Use CSMA selector. */
|
||||
} else {
|
||||
val = 0;
|
||||
dev->cLinkDownReported++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
/* Auto negotiation expansion register. */
|
||||
if (!isolate) {
|
||||
if (dev->fLinkUp && !dev->fLinkTempDown && !isolate) {
|
||||
val = 0x0008 /* Link partner supports npage. */
|
||||
| 0x0004 /* Enable npage words. */
|
||||
| 0x0001; /* Can do N-way auto-negotiation. */
|
||||
} else {
|
||||
val = 0;
|
||||
dev->cLinkDownReported++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 18:
|
||||
/* Diagnostic Register (FreeBSD pcn/ac101 driver reads this). */
|
||||
if (!isolate) {
|
||||
if (dev->fLinkUp && !dev->fLinkTempDown && !isolate) {
|
||||
val = 0x1000 /* Receive PLL locked. */
|
||||
| 0x0200; /* Signal detected. */
|
||||
|
||||
@@ -2095,6 +2159,7 @@ pcnet_mii_readw(nic_t *dev, uint16_t miiaddr)
|
||||
}
|
||||
} else {
|
||||
val = 0;
|
||||
dev->cLinkDownReported++;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2117,6 +2182,11 @@ pcnet_bcr_readw(nic_t *dev, uint16_t rap)
|
||||
case BCR_LED2:
|
||||
case BCR_LED3:
|
||||
val = dev->aBCR[rap] & ~0x8000;
|
||||
if (dev->fLinkTempDown || !dev->fLinkUp) {
|
||||
if (rap == 4)
|
||||
dev->cLinkDownReported++;
|
||||
val &= ~0x40;
|
||||
}
|
||||
val |= (val & 0x017f & dev->u32Lnkst) ? 0x8000 : 0;
|
||||
break;
|
||||
|
||||
@@ -2623,6 +2693,27 @@ pcnet_pci_read(int func, int addr, void *p)
|
||||
return(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes down the link temporarily if it's current status is up.
|
||||
*
|
||||
* This is used during restore and when replumbing the network link.
|
||||
*
|
||||
* The temporary link outage is supposed to indicate to the OS that all network
|
||||
* connections have been lost and that it for instance is appropriate to
|
||||
* renegotiate any DHCP lease.
|
||||
*
|
||||
* @param pThis The PCnet shared instance data.
|
||||
*/
|
||||
static void
|
||||
pcnetTempLinkDown(nic_t *dev)
|
||||
{
|
||||
if (dev->fLinkUp) {
|
||||
dev->fLinkTempDown = 1;
|
||||
dev->cLinkDownReported = 0;
|
||||
dev->aCSR[0] |= 0x8000 | 0x2000; /* ERR | CERR (this is probably wrong) */
|
||||
timer_set_delay_u64(&dev->timer_restore, (dev->cMsLinkUpDelay * 1000) * TIMER_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device/driver can receive data now.
|
||||
@@ -2664,6 +2755,34 @@ pcnetWaitReceiveAvail(void *priv)
|
||||
return dev->fMaybeOutOfSpace;
|
||||
}
|
||||
|
||||
static int
|
||||
pcnetSetLinkState(void *priv)
|
||||
{
|
||||
nic_t *dev = (nic_t *) priv;
|
||||
int fLinkUp;
|
||||
|
||||
if (dev->fLinkTempDown) {
|
||||
pcnetTempLinkDown(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fLinkUp = (dev->fLinkUp && !dev->fLinkTempDown);
|
||||
if (dev->fLinkUp != fLinkUp) {
|
||||
dev->fLinkUp = fLinkUp;
|
||||
if (fLinkUp) {
|
||||
dev->fLinkTempDown = 1;
|
||||
dev->cLinkDownReported = 0;
|
||||
dev->aCSR[0] |= 0x8000 | 0x2000; /* ERR | CERR (this is probably wrong) */
|
||||
timer_set_delay_u64(&dev->timer_restore, (dev->cMsLinkUpDelay * 1000) * TIMER_USEC);
|
||||
} else {
|
||||
dev->cLinkDownReported = 0;
|
||||
dev->aCSR[0] |= 0x8000 | 0x2000; /* ERR | CERR (this is probably wrong) */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pcnetTimerSoftInt(void *priv)
|
||||
{
|
||||
@@ -2675,16 +2794,18 @@ pcnetTimerSoftInt(void *priv)
|
||||
}
|
||||
|
||||
static void
|
||||
pcnetTimerCallback(void *priv)
|
||||
pcnetTimerRestore(void *priv)
|
||||
{
|
||||
nic_t *dev = (nic_t *) priv;
|
||||
|
||||
#ifdef ENABLE_PCNET_LOG
|
||||
pcnetlog(3, "Timer Callback to RX\n");
|
||||
#endif
|
||||
pcnetPollRxTx(dev);
|
||||
|
||||
timer_disable(&dev->poll_timer);
|
||||
if (dev->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED) {
|
||||
timer_advance_u64(&dev->timer_restore, 1500000 * TIMER_USEC);
|
||||
} else {
|
||||
dev->fLinkTempDown = 0;
|
||||
if (dev->fLinkUp) {
|
||||
dev->aCSR[0] &= ~(0x8000 | 0x2000); /* ERR | CERR - probably not 100% correct either... */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
@@ -2718,9 +2839,18 @@ pcnet_init(const device_t *info)
|
||||
pcnet_mem_disable(dev);
|
||||
}
|
||||
|
||||
dev->fLinkUp = 1;
|
||||
dev->cMsLinkUpDelay = 5000;
|
||||
|
||||
if (dev->board == DEV_AM79C960_EB) {
|
||||
dev->maclocal[0] = 0x02; /* 02:07:01 (Racal OID) */
|
||||
dev->maclocal[1] = 0x07;
|
||||
dev->maclocal[2] = 0x01;
|
||||
} else {
|
||||
dev->maclocal[0] = 0x00; /* 00:0C:87 (AMD OID) */
|
||||
dev->maclocal[1] = 0x0C;
|
||||
dev->maclocal[2] = 0x87;
|
||||
}
|
||||
|
||||
/* See if we have a local MAC address configured. */
|
||||
mac = device_get_config_mac("mac", -1);
|
||||
@@ -2816,12 +2946,12 @@ pcnet_init(const device_t *info)
|
||||
pcnetHardReset(dev);
|
||||
|
||||
/* Attach ourselves to the network module. */
|
||||
network_attach(dev, dev->aPROM, pcnetReceiveNoSync, pcnetWaitReceiveAvail);
|
||||
network_attach(dev, dev->aPROM, pcnetReceiveNoSync, pcnetWaitReceiveAvail, pcnetSetLinkState);
|
||||
|
||||
if (dev->board == DEV_AM79C973)
|
||||
timer_add(&dev->timer_soft_int, pcnetTimerSoftInt, dev, 0);
|
||||
|
||||
timer_add(&dev->poll_timer, pcnetTimerCallback, dev, 0);
|
||||
timer_add(&dev->timer_restore, pcnetTimerRestore, dev, 0);
|
||||
|
||||
return(dev);
|
||||
}
|
||||
@@ -2838,8 +2968,6 @@ pcnet_close(void *priv)
|
||||
network_close();
|
||||
|
||||
if (dev) {
|
||||
timer_disable(&dev->poll_timer);
|
||||
|
||||
free(dev);
|
||||
dev = NULL;
|
||||
|
||||
|
@@ -147,7 +147,7 @@ poll_thread(void *arg)
|
||||
/* Wait for the next packet to arrive. */
|
||||
data_valid = 0;
|
||||
|
||||
if ((!network_get_wait() && !(poll_card->wait && poll_card->wait(poll_card->priv))) && (QueuePeek(slirpq) != 0)) {
|
||||
if ((!network_get_wait() && !(poll_card->set_link_state && poll_card->set_link_state(poll_card->priv)) && !(poll_card->wait && poll_card->wait(poll_card->priv))) && (QueuePeek(slirpq) != 0)) {
|
||||
/* Grab a packet from the queue. */
|
||||
// ui_sb_update_icon(SB_NETWORK, 1);
|
||||
|
||||
|
@@ -771,7 +771,7 @@ wd_init(const device_t *info)
|
||||
mem_mapping_disable(&dev->ram_mapping);
|
||||
|
||||
/* Attach ourselves to the network module. */
|
||||
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL);
|
||||
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL);
|
||||
|
||||
if (!(dev->board_chip & WE_ID_BUS_MCA)) {
|
||||
wdlog("%s: attached IO=0x%X IRQ=%d, RAM addr=0x%06x\n", dev->name,
|
||||
|
@@ -223,7 +223,7 @@ network_init(void)
|
||||
* modules.
|
||||
*/
|
||||
void
|
||||
network_attach(void *dev, uint8_t *mac, NETRXCB rx, NETWAITCB wait)
|
||||
network_attach(void *dev, uint8_t *mac, NETRXCB rx, NETWAITCB wait, NETSETLINKSTATE set_link_state)
|
||||
{
|
||||
if (network_card == 0) return;
|
||||
|
||||
@@ -231,6 +231,7 @@ network_attach(void *dev, uint8_t *mac, NETRXCB rx, NETWAITCB wait)
|
||||
net_cards[network_card].priv = dev;
|
||||
net_cards[network_card].rx = rx;
|
||||
net_cards[network_card].wait = wait;
|
||||
net_cards[network_card].set_link_state = set_link_state;
|
||||
network_mac = mac;
|
||||
|
||||
network_set_wait(0);
|
||||
|
Reference in New Issue
Block a user