Patchwork [v2] pcnet: Properly handle TX requests during Link Fail

login
register
mail settings
Submitter Jan Kiszka
Date March 26, 2012, 12:21 p.m.
Message ID <4F705F31.7030301@siemens.com>
Download mbox | patch
Permalink /patch/148712/
State New
Headers show

Comments

Jan Kiszka - March 26, 2012, 12:21 p.m.
As long as we have no link and we aren't in internal loopback mode, no
packet must be sent. Instead, LCAR needs to be set in any active TX
descriptor and also CERR in CSR0.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---

Change in v2:
 - We were lacking a reset of xmit_pos in case of a link-down error.
   That caused bogus underflow errors.

 hw/pcnet.c |   11 +++++++++++
 hw/pcnet.h |    1 +
 2 files changed, 12 insertions(+), 0 deletions(-)

Patch

diff --git a/hw/pcnet.c b/hw/pcnet.c
index 7413409..d769b08 100644
--- a/hw/pcnet.c
+++ b/hw/pcnet.c
@@ -77,6 +77,7 @@  struct qemu_ether_header {
 #define CSR_DTX(S)       !!(((S)->csr[15])&0x0002)
 #define CSR_LOOP(S)      !!(((S)->csr[15])&0x0004)
 #define CSR_DXMTFCS(S)   !!(((S)->csr[15])&0x0008)
+#define CSR_INTL(S)      !!(((S)->csr[15])&0x0040)
 #define CSR_DRCVPA(S)    !!(((S)->csr[15])&0x2000)
 #define CSR_DRCVBC(S)    !!(((S)->csr[15])&0x4000)
 #define CSR_PROM(S)      !!(((S)->csr[15])&0x8000)
@@ -1234,6 +1235,15 @@  static void pcnet_transmit(PCNetState *s)
             if (BCR_SWSTYLE(s) != 1)
                 add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
         }
+        if (s->lnkst == 0 &&
+            (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
+            SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
+            SET_FIELD(&tmd.status, TMDS, ERR, 1);
+            SET_FIELD(&tmd.status, TMDS, OWN, 0);
+            s->csr[0] |= 0xa000; /* ERR | CERR */
+            s->xmit_pos = -1;
+            goto txdone;
+        }
         if (!GET_FIELD(tmd.status, TMDS, ENP)) {
             int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
             s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
@@ -1262,6 +1272,7 @@  static void pcnet_transmit(PCNetState *s)
             s->xmit_pos = -1;
         }

+    txdone:
         SET_FIELD(&tmd.status, TMDS, OWN, 0);
         TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
         if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status,
TMDS, LTINT)))
diff --git a/hw/pcnet.h b/hw/pcnet.h
index edc81c9..803a2cc 100644
--- a/hw/pcnet.h
+++ b/hw/pcnet.h
@@ -20,6 +20,7 @@ 
 #define BCR_SWS      20
 #define BCR_PLAT     22

+#define BCR_TMAULOOP(S)  !!((S)->bcr[BCR_MC  ] & 0x4000)
 #define BCR_APROMWE(S)   !!((S)->bcr[BCR_MC  ] & 0x0100)
 #define BCR_DWIO(S)      !!((S)->bcr[BCR_BSBC] & 0x0080)
 #define BCR_SSIZE32(S)   !!((S)->bcr[BCR_SWS ] & 0x0100)