$NetBSD: patch-cq,v 1.1.1.1 2007/06/14 19:42:12 bouyer Exp $ This is a backport of bugfixes and changes in xen-unstable to make pcnet usable. It gives better performances than the ne2000 or realtek emulations. --- ioemu/hw/pcnet.c.orig 2007-06-13 19:06:54.000000000 +0200 +++ ioemu/hw/pcnet.c 2007-06-13 19:54:43.000000000 +0200 @@ -41,25 +41,6 @@ #define PCNET_IOPORT_SIZE 0x20 #define PCNET_PNPMMIO_SIZE 0x20 - -typedef struct PCNetState_st PCNetState; - -struct PCNetState_st { - PCIDevice dev; - VLANClientState *vc; - NICInfo *nd; - QEMUTimer *poll_timer; - int mmio_io_addr, rap, isr, lnkst; - target_phys_addr_t rdra, tdra; - uint8_t prom[16]; - uint16_t csr[128]; - uint16_t bcr[32]; - uint64_t timer; - int xmit_pos, recv_pos; - uint8_t buffer[4096]; - int tx_busy; -}; - /* XXX: using bitfields for target memory structures is almost surely not portable, so it should be suppressed ASAP */ #ifdef __GNUC__ @@ -251,6 +232,28 @@ (R)->rmd2.rcc, (R)->rmd2.rpc, (R)->rmd2.mcnt, \ (R)->rmd2.zeros) +typedef struct PCNetState_st PCNetState; + +struct PCNetState_st { + PCIDevice dev; + VLANClientState *vc; + NICInfo *nd; + QEMUTimer *poll_timer; + int mmio_io_addr, rap, isr, lnkst; + target_phys_addr_t rdra, tdra; + uint8_t prom[16]; + uint16_t csr[128]; + uint16_t bcr[32]; + uint64_t timer; + int recv_pos; + uint8_t tx_buffer[2048]; + uint8_t rx_buffer[2048]; + struct pcnet_TMD tmd; + struct pcnet_RMD crmd; + struct pcnet_RMD nrmd; + struct pcnet_RMD nnrmd; +}; + static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd, target_phys_addr_t addr) { if (!BCR_SWSTYLE(s)) { @@ -269,18 +272,17 @@ if (BCR_SWSTYLE(s) != 3) cpu_physical_memory_read(addr, (void *)tmd, 16); else { - uint32_t xda[4]; - cpu_physical_memory_read(addr, - (void *)&xda[0], sizeof(xda)); - ((uint32_t *)tmd)[0] = xda[2]; - ((uint32_t *)tmd)[1] = xda[1]; - ((uint32_t *)tmd)[2] = xda[0]; - ((uint32_t *)tmd)[3] = xda[3]; + uint32_t xda[2]; + cpu_physical_memory_read(addr+4, (void *)&xda[0], sizeof(xda)); + ((uint32_t *)tmd)[0] = xda[1]; + ((uint32_t *)tmd)[1] = xda[0]; + ((uint32_t *)tmd)[2] = 0; } } static inline void pcnet_tmd_store(PCNetState *s, struct pcnet_TMD *tmd, target_phys_addr_t addr) { + tmd->tmd1.own = 0; if (!BCR_SWSTYLE(s)) { uint16_t xda[4]; xda[0] = ((uint32_t *)tmd)[0] & 0xffff; @@ -295,14 +297,12 @@ if (BCR_SWSTYLE(s) != 3) cpu_physical_memory_write(addr, (void *)tmd, 16); else { - uint32_t xda[4]; + uint32_t xda[2]; xda[0] = ((uint32_t *)tmd)[2]; xda[1] = ((uint32_t *)tmd)[1]; - xda[2] = ((uint32_t *)tmd)[0]; - xda[3] = ((uint32_t *)tmd)[3]; - cpu_physical_memory_write(addr, - (void *)&xda[0], sizeof(xda)); + cpu_physical_memory_write(addr, (void *)&xda[0], sizeof(xda)); } + cpu_physical_memory_set_dirty(addr+15); } } @@ -320,21 +320,22 @@ ((uint32_t *)rmd)[3] = 0; } else - if (BCR_SWSTYLE(s) != 3) + if (BCR_SWSTYLE(s) != 3) { + ((uint32_t *)rmd)[2] = 0; cpu_physical_memory_read(addr, (void *)rmd, 16); - else { - uint32_t rda[4]; - cpu_physical_memory_read(addr, - (void *)&rda[0], sizeof(rda)); - ((uint32_t *)rmd)[0] = rda[2]; - ((uint32_t *)rmd)[1] = rda[1]; - ((uint32_t *)rmd)[2] = rda[0]; - ((uint32_t *)rmd)[3] = rda[3]; + } else { + uint32_t rda[2]; + cpu_physical_memory_read(addr+4, (void *)&rda[0], sizeof(rda)); + ((uint32_t *)rmd)[0] = rda[1]; + ((uint32_t *)rmd)[1] = rda[0]; + ((uint32_t *)rmd)[2] = 0; } } static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd, target_phys_addr_t addr) { + rmd->rmd1.own = 0; + cpu_physical_memory_set_dirty(addr); if (!BCR_SWSTYLE(s)) { uint16_t rda[4]; \ rda[0] = ((uint32_t *)rmd)[0] & 0xffff; \ @@ -349,13 +350,10 @@ if (BCR_SWSTYLE(s) != 3) cpu_physical_memory_write(addr, (void *)rmd, 16); else { - uint32_t rda[4]; + uint32_t rda[2]; rda[0] = ((uint32_t *)rmd)[2]; rda[1] = ((uint32_t *)rmd)[1]; - rda[2] = ((uint32_t *)rmd)[0]; - rda[3] = ((uint32_t *)rmd)[3]; - cpu_physical_memory_write(addr, - (void *)&rda[0], sizeof(rda)); + cpu_physical_memory_write(addr, (void *)&rda[0], sizeof(rda)); } } } @@ -369,81 +367,16 @@ #define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR) -#if 1 - -#define CHECK_RMD(ADDR,RES) do { \ - struct pcnet_RMD rmd; \ - RMDLOAD(&rmd,(ADDR)); \ - (RES) |= (rmd.rmd1.ones != 15) \ - || (rmd.rmd2.zeros != 0); \ +#define CHECK_RMD(RMD,ADDR,RES) do { \ + RMDLOAD((RMD),(ADDR)); \ + (RES) |= ((RMD)->rmd1.ones != 15); \ } while (0) #define CHECK_TMD(ADDR,RES) do { \ - struct pcnet_TMD tmd; \ - TMDLOAD(&tmd,(ADDR)); \ - (RES) |= (tmd.tmd1.ones != 15); \ + TMDLOAD(&(s->tmd),(ADDR)); \ + (RES) |= (s->tmd.tmd1.ones != 15); \ } while (0) -#else - -#define CHECK_RMD(ADDR,RES) do { \ - switch (BCR_SWSTYLE(s)) { \ - case 0x00: \ - do { \ - uint16_t rda[4]; \ - cpu_physical_memory_read((ADDR), \ - (void *)&rda[0], sizeof(rda)); \ - (RES) |= (rda[2] & 0xf000)!=0xf000; \ - (RES) |= (rda[3] & 0xf000)!=0x0000; \ - } while (0); \ - break; \ - case 0x01: \ - case 0x02: \ - do { \ - uint32_t rda[4]; \ - cpu_physical_memory_read((ADDR), \ - (void *)&rda[0], sizeof(rda)); \ - (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \ - (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \ - } while (0); \ - break; \ - case 0x03: \ - do { \ - uint32_t rda[4]; \ - cpu_physical_memory_read((ADDR), \ - (void *)&rda[0], sizeof(rda)); \ - (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \ - (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \ - } while (0); \ - break; \ - } \ -} while (0) - -#define CHECK_TMD(ADDR,RES) do { \ - switch (BCR_SWSTYLE(s)) { \ - case 0x00: \ - do { \ - uint16_t xda[4]; \ - cpu_physical_memory_read((ADDR), \ - (void *)&xda[0], sizeof(xda)); \ - (RES) |= (xda[2] & 0xf000)!=0xf000;\ - } while (0); \ - break; \ - case 0x01: \ - case 0x02: \ - case 0x03: \ - do { \ - uint32_t xda[4]; \ - cpu_physical_memory_read((ADDR), \ - (void *)&xda[0], sizeof(xda)); \ - (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \ - } while (0); \ - break; \ - } \ -} while (0) - -#endif - #define PRINT_PKTHDR(BUF) do { \ struct qemu_ether_header *hdr = (void *)(BUF); \ printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \ @@ -662,8 +595,6 @@ s->csr[114] = 0x0000; s->csr[122] = 0x0000; s->csr[124] = 0x0000; - - s->tx_busy = 0; } static void pcnet_update_irq(PCNetState *s) @@ -737,6 +668,9 @@ s->csr[15] = le16_to_cpu(initblk.mode); \ CSR_RCVRL(s) = (initblk.rlen < 9) ? (1 << initblk.rlen) : 512; \ CSR_XMTRL(s) = (initblk.tlen < 9) ? (1 << initblk.tlen) : 512; \ + s->crmd.rmd1.own = 0; \ + s->nrmd.rmd1.own = 0; \ + s->nnrmd.rmd1.own = 0; \ s->csr[ 6] = (initblk.tlen << 12) | (initblk.rlen << 8); \ s->csr[ 8] = le16_to_cpu(initblk.ladrf1); \ s->csr[ 9] = le16_to_cpu(initblk.ladrf2); \ @@ -791,6 +725,10 @@ if (!CSR_DRX(s)) s->csr[0] |= 0x0020; /* set RXON */ + /* flush any cached receive descriptors */ + s->crmd.rmd1.own = 0; + s->nrmd.rmd1.own = 0; + s->nnrmd.rmd1.own = 0; s->csr[0] &= ~0x0004; /* clear STOP bit */ s->csr[0] |= 0x0002; @@ -813,29 +751,21 @@ s->csr[28] = s->csr[29] = 0; if (s->rdra) { int bad = 0; -#if 1 target_phys_addr_t crda = pcnet_rdra_addr(s, CSR_RCVRC(s)); target_phys_addr_t nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s)); target_phys_addr_t nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s)); -#else - target_phys_addr_t crda = s->rdra + - (CSR_RCVRL(s) - CSR_RCVRC(s)) * - (BCR_SWSTYLE(s) ? 16 : 8 ); - int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1; - target_phys_addr_t nrda = s->rdra + - (CSR_RCVRL(s) - nrdc) * - (BCR_SWSTYLE(s) ? 16 : 8 ); - int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1; - target_phys_addr_t nnrd = s->rdra + - (CSR_RCVRL(s) - nnrc) * - (BCR_SWSTYLE(s) ? 16 : 8 ); -#endif - CHECK_RMD(PHYSADDR(s,crda), bad); + if (!s->crmd.rmd1.own) { + CHECK_RMD(&(s->crmd),PHYSADDR(s,crda), bad); + } if (!bad) { - CHECK_RMD(PHYSADDR(s,nrda), bad); + if (s->crmd.rmd1.own && !s->nrmd.rmd1.own) { + CHECK_RMD(&(s->nrmd),PHYSADDR(s,nrda), bad); + } if (bad || (nrda == crda)) nrda = 0; - CHECK_RMD(PHYSADDR(s,nnrd), bad); + if (s->crmd.rmd1.own && s->nrmd.rmd1.own && !s->nnrmd.rmd1.own) { + CHECK_RMD(&(s->nnrmd),PHYSADDR(s,nnrd), bad); + } if (bad || (nnrd == crda)) nnrd = 0; s->csr[28] = crda & 0xffff; @@ -856,14 +786,12 @@ } if (CSR_CRDA(s)) { - struct pcnet_RMD rmd; - RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s))); - CSR_CRBC(s) = rmd.rmd1.bcnt; - CSR_CRST(s) = ((uint32_t *)&rmd)[1] >> 16; + CSR_CRBC(s) = s->crmd.rmd1.bcnt; + CSR_CRST(s) = ((uint32_t *)&(s->crmd))[1] >> 16; #ifdef PCNET_DEBUG_RMD_X printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMD1=0x%08x RMD2=0x%08x\n", PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s), - ((uint32_t *)&rmd)[1], ((uint32_t *)&rmd)[2]); + ((uint32_t *)&(s->crmd))[1], ((uint32_t *)&(s->crmd))[2]); PRINT_RMD(&rmd); #endif } else { @@ -871,10 +799,8 @@ } if (CSR_NRDA(s)) { - struct pcnet_RMD rmd; - RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s))); - CSR_NRBC(s) = rmd.rmd1.bcnt; - CSR_NRST(s) = ((uint32_t *)&rmd)[1] >> 16; + CSR_NRBC(s) = s->nrmd.rmd1.bcnt; + CSR_NRST(s) = ((uint32_t *)&(s->nrmd))[1] >> 16; } else { CSR_NRBC(s) = CSR_NRST(s) = 0; } @@ -889,6 +815,7 @@ (CSR_XMTRL(s) - CSR_XMTRC(s)) * (BCR_SWSTYLE(s) ? 16 : 8 ); int bad = 0; + s->csr[0] &= ~0x0008; /* clear TDMD */ CHECK_TMD(PHYSADDR(s, cxda),bad); if (!bad) { if (CSR_CXDA(s) != cxda) { @@ -907,12 +834,8 @@ } if (CSR_CXDA(s)) { - struct pcnet_TMD tmd; - - TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s))); - - CSR_CXBC(s) = tmd.tmd1.bcnt; - CSR_CXST(s) = ((uint32_t *)&tmd)[1] >> 16; + CSR_CXBC(s) = s->tmd.tmd1.bcnt; + CSR_CXST(s) = ((uint32_t *)&(s->tmd))[1] >> 16; } else { CSR_CXBC(s) = CSR_CXST(s) = 0; } @@ -925,11 +848,12 @@ PCNetState *s = opaque; if (CSR_STOP(s) || CSR_SPND(s)) return 0; - - if (s->recv_pos > 0) - return 0; - return sizeof(s->buffer)-16; + if (!(CSR_CRST(s) & 0x8000)) { + return 0; + } + + return sizeof(s->rx_buffer)-16; } #define MIN_BUF_SIZE 60 @@ -938,7 +862,7 @@ { PCNetState *s = opaque; int is_padr = 0, is_bcast = 0, is_ladr = 0; - uint8_t buf1[60]; + int pad; if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size) return; @@ -948,12 +872,10 @@ #endif /* if too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); - buf = buf1; - size = MIN_BUF_SIZE; - } + if (size < MIN_BUF_SIZE) + pad = MIN_BUF_SIZE - size + 4; + else + pad = 4; if (CSR_PROM(s) || (is_padr=padr_match(s, buf, size)) @@ -962,125 +884,75 @@ pcnet_rdte_poll(s); - if (!(CSR_CRST(s) & 0x8000) && s->rdra) { - struct pcnet_RMD rmd; - int rcvrc = CSR_RCVRC(s)-1,i; - target_phys_addr_t nrda; - for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) { - if (rcvrc <= 1) - rcvrc = CSR_RCVRL(s); - nrda = s->rdra + - (CSR_RCVRL(s) - rcvrc) * - (BCR_SWSTYLE(s) ? 16 : 8 ); - RMDLOAD(&rmd, PHYSADDR(s,nrda)); - if (rmd.rmd1.own) { + if (size > 2000) { #ifdef PCNET_DEBUG_RMD - printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n", - rcvrc, CSR_RCVRC(s)); + printf("pcnet - oversize packet discarded.\n"); #endif - CSR_RCVRC(s) = rcvrc; - pcnet_rdte_poll(s); - break; - } - } - } - - if (!(CSR_CRST(s) & 0x8000)) { + } else if (!(CSR_CRST(s) & 0x8000)) { #ifdef PCNET_DEBUG_RMD printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s)); #endif s->csr[0] |= 0x1000; /* Set MISS flag */ CSR_MISSC(s)++; } else { - uint8_t *src = &s->buffer[8]; + uint8_t *src = &s->rx_buffer[8]; target_phys_addr_t crda = CSR_CRDA(s); - struct pcnet_RMD rmd; + target_phys_addr_t nrda = CSR_NRDA(s); + target_phys_addr_t nnrda = CSR_NNRD(s); int pktcount = 0; + int packet_size = size + pad; memcpy(src, buf, size); -#if 1 - /* no need to compute the CRC */ - src[size] = 0; - src[size + 1] = 0; - src[size + 2] = 0; - src[size + 3] = 0; - size += 4; -#else - /* XXX: avoid CRC generation */ - if (!CSR_ASTRP_RCV(s)) { - uint32_t fcs = ~0; - uint8_t *p = src; - - while (size < 46) { - src[size++] = 0; - } - - while (p != &src[size]) { - CRC(fcs, *p++); - } - ((uint32_t *)&src[size])[0] = htonl(fcs); - size += 4; /* FCS at end of packet */ - } else size += 4; -#endif + memset(src + size, 0, pad); + size += pad; #ifdef PCNET_DEBUG_MATCH PRINT_PKTHDR(buf); #endif - RMDLOAD(&rmd, PHYSADDR(s,crda)); - /*if (!CSR_LAPPEN(s))*/ - rmd.rmd1.stp = 1; - -#define PCNET_RECV_STORE() do { \ - int count = MIN(4096 - rmd.rmd1.bcnt,size); \ - target_phys_addr_t rbadr = PHYSADDR(s, rmd.rmd0.rbadr); \ - cpu_physical_memory_write(rbadr, src, count); \ - src += count; size -= count; \ - rmd.rmd2.mcnt = count; rmd.rmd1.own = 0; \ - RMDSTORE(&rmd, PHYSADDR(s,crda)); \ - pktcount++; \ -} while (0) - - PCNET_RECV_STORE(); - if ((size > 0) && CSR_NRDA(s)) { - target_phys_addr_t nrda = CSR_NRDA(s); - RMDLOAD(&rmd, PHYSADDR(s,nrda)); - if (rmd.rmd1.own) { + s->crmd.rmd1.stp = 1; + do { + int count = MIN(4096 - s->crmd.rmd1.bcnt,size); + target_phys_addr_t rbadr = PHYSADDR(s, s->crmd.rmd0.rbadr); + cpu_physical_memory_write(rbadr, src, count); + cpu_physical_memory_set_dirty(rbadr); + cpu_physical_memory_set_dirty(rbadr+count); + src += count; size -= count; + if (size > 0 && s->nrmd.rmd1.own) { + RMDSTORE(&(s->crmd), PHYSADDR(s,crda)); crda = nrda; - PCNET_RECV_STORE(); - if ((size > 0) && (nrda=CSR_NNRD(s))) { - RMDLOAD(&rmd, PHYSADDR(s,nrda)); - if (rmd.rmd1.own) { - crda = nrda; - PCNET_RECV_STORE(); - } - } - } - } - -#undef PCNET_RECV_STORE + nrda = nnrda; + s->crmd = s->nrmd; + s->nrmd = s->nnrmd; + s->nnrmd.rmd1.own = 0; + } + pktcount++; + } while (size > 0 && s->crmd.rmd1.own); - RMDLOAD(&rmd, PHYSADDR(s,crda)); if (size == 0) { - rmd.rmd1.enp = 1; - rmd.rmd1.pam = !CSR_PROM(s) && is_padr; - rmd.rmd1.lafm = !CSR_PROM(s) && is_ladr; - rmd.rmd1.bam = !CSR_PROM(s) && is_bcast; + s->crmd.rmd1.enp = 1; + s->crmd.rmd2.mcnt = packet_size; + s->crmd.rmd1.pam = !CSR_PROM(s) && is_padr; + s->crmd.rmd1.lafm = !CSR_PROM(s) && is_ladr; + s->crmd.rmd1.bam = !CSR_PROM(s) && is_bcast; } else { - rmd.rmd1.oflo = 1; - rmd.rmd1.buff = 1; - rmd.rmd1.err = 1; + s->crmd.rmd1.oflo = 1; + s->crmd.rmd1.buff = 1; + s->crmd.rmd1.err = 1; } - RMDSTORE(&rmd, PHYSADDR(s,crda)); + RMDSTORE(&(s->crmd), PHYSADDR(s,crda)); s->csr[0] |= 0x0400; + s->crmd = s->nrmd; + s->nrmd = s->nnrmd; + s->nnrmd.rmd1.own = 0; #ifdef PCNET_DEBUG printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n", CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount); #endif #ifdef PCNET_DEBUG_RMD - PRINT_RMD(&rmd); + PRINT_RMD(&s->crmd); #endif while (pktcount--) { @@ -1101,81 +973,86 @@ static void pcnet_transmit(PCNetState *s) { - target_phys_addr_t xmit_cxda = 0; + target_phys_addr_t start_addr = 0; + struct pcnet_TMD start_tmd; int count = CSR_XMTRL(s)-1; - s->xmit_pos = -1; + int xmit_pos = 0; + int len; 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))); + while (pcnet_tdte_poll(s)) { #ifdef PCNET_DEBUG_TMD printf(" TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s))); - PRINT_TMD(&tmd); + PRINT_TMD(&(s->tmd)); #endif - if (tmd.tmd1.stp) { - s->xmit_pos = 0; - if (!tmd.tmd1.enp) { - cpu_physical_memory_read(PHYSADDR(s, tmd.tmd0.tbadr), - s->buffer, 4096 - tmd.tmd1.bcnt); - s->xmit_pos += 4096 - tmd.tmd1.bcnt; - } - xmit_cxda = PHYSADDR(s,CSR_CXDA(s)); - } - if (tmd.tmd1.enp && (s->xmit_pos >= 0)) { - cpu_physical_memory_read(PHYSADDR(s, tmd.tmd0.tbadr), - s->buffer + s->xmit_pos, 4096 - tmd.tmd1.bcnt); - s->xmit_pos += 4096 - tmd.tmd1.bcnt; -#ifdef PCNET_DEBUG - printf("pcnet_transmit size=%d\n", s->xmit_pos); -#endif - if (CSR_LOOP(s)) - pcnet_receive(s, s->buffer, s->xmit_pos); - else - qemu_send_packet(s->vc, s->buffer, s->xmit_pos); - - s->csr[0] &= ~0x0008; /* clear TDMD */ - s->csr[4] |= 0x0004; /* set TXSTRT */ - s->xmit_pos = -1; - } + len = 4096 - s->tmd.tmd1.bcnt; + if (CSR_XMTRC(s) <= 1) + CSR_XMTRC(s) = CSR_XMTRL(s); + else + CSR_XMTRC(s)--; + /* handle start followed by start */ + if (s->tmd.tmd1.stp && start_addr) { + TMDSTORE(&start_tmd, start_addr); + start_addr = 0; + xmit_pos = 0; + } + if ((xmit_pos + len) < sizeof(s->tx_buffer)) { + cpu_physical_memory_read(PHYSADDR(s, s->tmd.tmd0.tbadr), + s->tx_buffer + xmit_pos, len); + xmit_pos += len; + } else { + s->tmd.tmd2.buff = s->tmd.tmd2.uflo = s->tmd.tmd1.err = 1; + TMDSTORE(&(s->tmd), PHYSADDR(s,CSR_CXDA(s))); + if (start_addr == PHYSADDR(s,CSR_CXDA(s))) + start_addr = 0; /* don't clear own bit twice */ + continue; + } + if (s->tmd.tmd1.stp) { + if (s->tmd.tmd1.enp) { + if (CSR_LOOP(s)) + pcnet_receive(s, s->tx_buffer, xmit_pos); + else + qemu_send_packet(s->vc, s->tx_buffer, xmit_pos); + + s->csr[4] |= 0x0008; /* set TXSTRT */ + TMDSTORE(&(s->tmd), PHYSADDR(s,CSR_CXDA(s))); + xmit_pos = 0; + count--; + } else { + start_tmd = s->tmd; + start_addr = PHYSADDR(s,CSR_CXDA(s)); + } + } else if (s->tmd.tmd1.enp) { + TMDSTORE(&(s->tmd), PHYSADDR(s,CSR_CXDA(s))); + if (start_addr) { + TMDSTORE(&start_tmd, start_addr); + } + start_addr = 0; + xmit_pos = 0; + count--; + } else { + TMDSTORE(&(s->tmd), PHYSADDR(s,CSR_CXDA(s))); + } - tmd.tmd1.own = 0; - TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); - if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && tmd.tmd1.ltint)) + if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && s->tmd.tmd1.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, PHYSADDR(s,xmit_cxda)); - tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1; - tmd.tmd1.own = 0; - TMDSTORE(&tmd, PHYSADDR(s,xmit_cxda)); - s->csr[0] |= 0x0200; /* set TINT */ - if (!CSR_DXSUFLO(s)) { - s->csr[0] &= ~0x0010; - } else - if (count--) - goto txagain; + if (count <= 0) + break; + } + if (start_addr) { + start_tmd.tmd2.buff = start_tmd.tmd2.uflo = start_tmd.tmd1.err = 1; + TMDSTORE(&start_tmd, PHYSADDR(s,start_addr)); + s->csr[0] |= 0x0200; /* set TINT */ + if (!CSR_DXSUFLO(s)) { + s->csr[0] &= ~0x0010; + } } - - s->tx_busy = 0; } static void pcnet_poll(PCNetState *s) @@ -1186,13 +1063,7 @@ if (CSR_TDMD(s) || (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) - { - /* prevent recursion */ - if (s->tx_busy) - return; - pcnet_transmit(s); - } } static void pcnet_poll_timer(void *opaque) @@ -1744,10 +1615,9 @@ for (i = 0; i < 32; i++) qemu_put_be16s(f, &s->bcr[i]); qemu_put_be64s(f, &s->timer); - qemu_put_be32s(f, &s->xmit_pos); qemu_put_be32s(f, &s->recv_pos); - qemu_put_buffer(f, s->buffer, 4096); - qemu_put_be32s(f, &s->tx_busy); + qemu_put_buffer(f, s->tx_buffer, 2048); + qemu_put_buffer(f, s->rx_buffer, 2048); qemu_put_timer(f, s->poll_timer); } @@ -1770,10 +1640,9 @@ for (i = 0; i < 32; i++) qemu_get_be16s(f, &s->bcr[i]); qemu_get_be64s(f, &s->timer); - qemu_get_be32s(f, &s->xmit_pos); qemu_get_be32s(f, &s->recv_pos); - qemu_get_buffer(f, s->buffer, 4096); - qemu_get_be32s(f, &s->tx_busy); + qemu_get_buffer(f, s->tx_buffer, 2048); + qemu_get_buffer(f, s->rx_buffer, 2048); qemu_get_timer(f, s->poll_timer); return 0;