static void pcnet_transmit(PCNetState *s) { hwaddr xmit_cxda = 0; int count = CSR_XMTRL(s)-1; int add_crc = 0; int bcnt; s->xmit_pos = -1; if (!CSR_TXON(s)) { s->csr[0] &= ~0x0008; return; } s->tx_busy = 1; txagain: if (pcnet_tdte_poll(s)) { struct pcnet_TMD tmd; TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s))); #ifdef PCNET_DEBUG_TMD printf(" TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s))); PRINT_TMD(&tmd); #endif if (GET_FIELD(tmd.status, TMDS, STP)) { s->xmit_pos = 0; xmit_cxda = PHYSADDR(s,CSR_CXDA(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 (s->xmit_pos < 0) { goto txdone; } bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); /* if multi-tmd packet outsizes s->buffer then skip it silently. Note: this is not what real hw does */ if (s->xmit_pos + bcnt > sizeof(s->buffer)) { s->xmit_pos = -1; goto txdone; } s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); s->xmit_pos += bcnt; if (!GET_FIELD(tmd.status, TMDS, ENP)) { goto txdone; } #ifdef PCNET_DEBUG printf("pcnet_transmit size=%d\n", s->xmit_pos); #endif if (CSR_LOOP(s)) { if (BCR_SWSTYLE(s) == 1) add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); s->looptest = 0; } else { if (s->nic) { qemu_send_packet(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); } } s->csr[0] &= ~0x0008; /* clear TDMD */ s->csr[4] |= 0x0004; /* set TXSTRT */ 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))) s->csr[0] |= 0x0200; /* set TINT */ if (CSR_XMTRC(s)<=1) CSR_XMTRC(s) = CSR_XMTRL(s); else CSR_XMTRC(s)--; if (count--) goto txagain; } else if (s->xmit_pos >= 0) { struct pcnet_TMD tmd; TMDLOAD(&tmd, xmit_cxda); SET_FIELD(&tmd.misc, TMDM, BUFF, 1); SET_FIELD(&tmd.misc, TMDM, UFLO, 1); SET_FIELD(&tmd.status, TMDS, ERR, 1); SET_FIELD(&tmd.status, TMDS, OWN, 0); TMDSTORE(&tmd, xmit_cxda); s->csr[0] |= 0x0200; /* set TINT */ if (!CSR_DXSUFLO(s)) { s->csr[0] &= ~0x0010; } else if (count--) goto txagain; } s->tx_busy = 0; }