📄 i82586.c
字号:
sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, prev), IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur)); off = IE_SCB_STATUS(sc->scb); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); if ((sc->ie_bus_read16(sc, off) & IE_CUS_ACTIVE) == 0) { printf("iexmit: CU not active\n"); i82586_start_transceiver(sc); } } else { sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds,cur), 0xffff); sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur), IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST); off = IE_SCB_CMDLST(sc->scb); sc->ie_bus_write16(sc, off, IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur)); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); if (i82586_start_cmd(sc, IE_CUC_START, 0, 0, ASYNC_OPTION)) printf("%s: iexmit: start xmit command timed out\n", sc->arpcom.ac_if.if_name); } sc->arpcom.ac_if.if_timer = 5;}/* * Device timeout/watchdog routine. * Entered if the device neglects to generate an interrupt after a * transmit has been started on it. */voidi82586_watchdog(struct ifnet *ifp){ struct ie_softc *sc = ifp->if_softc; printf("%s: device timeout\n", ifp->if_name); ++ifp->if_oerrors; i82586_reset(sc, 1);}static inti82586_cmd_wait(struct ie_softc *sc){ /* spin on i82586 command acknowledge; wait at most 0.9 (!) seconds */ int i, off; u_int16_t cmd; for (i = 0; i < 900000; i++) { /* Read the command word */ off = IE_SCB_CMD(sc->scb); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); if ((cmd = sc->ie_bus_read16(sc, off)) == 0) return (0); delay(1); } off = IE_SCB_STATUS(sc->scb); printf("i82586_cmd_wait: timo(%ssync): scb status: 0x%x, cmd: 0x%x\n", sc->async_cmd_inprogress?"a":"", sc->ie_bus_read16(sc, off), cmd); return (1); /* Timeout */}/* * Send a command to the controller and wait for it to either complete * or be accepted, depending on the command. If the command pointer * is null, then pretend that the command is not an action command. * If the command pointer is not null, and the command is an action * command, wait for one of the MASK bits to turn on in the command's * status field. * If ASYNC is set, we just call the chip's attention and return. * We may have to wait for the command's acceptance later though. */static inti82586_start_cmd(struct ie_softc *sc, int cmd, int iecmdbuf, int mask, int async){ int i; int off; if (sc->async_cmd_inprogress != 0) { /* * If previous command was issued asynchronously, wait * for it now. */ if (i82586_cmd_wait(sc) != 0) return (1); sc->async_cmd_inprogress = 0; } off = IE_SCB_CMD(sc->scb); sc->ie_bus_write16(sc, off, cmd); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE); (sc->chan_attn)(sc, CARD_RESET); if (async != 0) { sc->async_cmd_inprogress = 1; return (0); } if (IE_ACTION_COMMAND(cmd) && iecmdbuf) { int status; /* * Now spin-lock waiting for status. This is not a very nice * thing to do, and can kill performance pretty well... * According to the packet driver, the minimum timeout * should be .369 seconds. */ for (i = 0; i < 369000; i++) { /* Read the command status */ off = IE_CMD_COMMON_STATUS(iecmdbuf); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); status = sc->ie_bus_read16(sc, off); if (status & mask) return (0); delay(1); } } else { /* * Otherwise, just wait for the command to be accepted. */ return (i82586_cmd_wait(sc)); } /* Timeout */ return (1);}/* * Transfer accumulated chip error counters to IF. */static __inline voidi82586_count_errors(struct ie_softc *sc){ int scb = sc->scb; sc->arpcom.ac_if.if_ierrors += sc->ie_bus_read16(sc, IE_SCB_ERRCRC(scb)) + sc->ie_bus_read16(sc, IE_SCB_ERRALN(scb)) + sc->ie_bus_read16(sc, IE_SCB_ERRRES(scb)) + sc->ie_bus_read16(sc, IE_SCB_ERROVR(scb)); /* Clear error counters */ sc->ie_bus_write16(sc, IE_SCB_ERRCRC(scb), 0); sc->ie_bus_write16(sc, IE_SCB_ERRALN(scb), 0); sc->ie_bus_write16(sc, IE_SCB_ERRRES(scb), 0); sc->ie_bus_write16(sc, IE_SCB_ERROVR(scb), 0);}static voidi82586_rx_errors(struct ie_softc *sc, int fn, int status){ char bits[128]; printf("%s: rx error (frame# %d): %s\n", sc->arpcom.ac_if.if_name, fn, bitmask_snprintf(status, IE_FD_STATUSBITS, bits, sizeof(bits)));}/* * i82586 interrupt entry point. */rtems_isri82586_intr(rtems_vector_number vec, void *arg){ struct ie_softc *sc = arg;#if I82586_DEBUG static unsigned long icnt = 0; I82586_TRACE(sc, I82586_INTS_REQ, icnt++);#endif /* * Implementation dependent interrupt handling. It must at least * disabled interrupts from the i82586. It is hoped this can * happen somewhere outside the i82586. */ if (sc->intrhook) (sc->intrhook)(sc, INTR_ENTER); /* * Wake the task to handle the interrupt. It will * enabled the interrupts when it has finished. */ rtems_event_send (sc->intr_task, i82586_WAKE_EVENT);}/* * i82586 interrupt task. The task is actually an extension of the interrupt * with a context switch in the middle. The RTEMS TCP/IP stack requires a task * be used to talk to the stack as a network semaphore is claimed. This * cannot happen during an interrupt. */static voidi82586_intr_task(void *arg){ struct ie_softc *sc = arg; rtems_event_set events; u_int status; int off; int reset; /* * Not sure this is a good idea but as a out path exists and * roads lead to it, it seems ok. */ for (;;) { rtems_bsdnet_event_receive (i82586_WAKE_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, 0, &events); off = IE_SCB_STATUS(sc->scb); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); status = sc->ie_bus_read16(sc, off) & IE_ST_WHENCE;#if I82586_DEBUG I82586_TRACE(sc, I82586_INTS_IN, status);#endif reset = 0; while ((status & IE_ST_WHENCE) != 0) {#if I82586_DEBUG if (sc->sc_debug) printf ("%s: -------\n%s: scbstatus=0x%x\n", sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_name, status);#endif #if 1 /* Ack interrupts FIRST in case we receive more during the ISR. */ ie_ack(sc, status & IE_ST_WHENCE);#endif i82586_start_cmd(sc, status & IE_ST_WHENCE, 0, 0, ASYNC_OPTION); if (status & (IE_ST_FR | IE_ST_RNR)) if (i82586_rint(sc, status) != 0) { reset = 1; break; } if (status & IE_ST_CX) if (i82586_tint(sc, status) != 0) { reset = 1; break; }#if I82586_DEBUG if ((status & IE_ST_CNA) && (sc->sc_debug & IED_CNA)) printf("%s: cna; status=0x%x\n", sc->arpcom.ac_if.if_name, status);#endif#if 0 if (sc->intrhook) (sc->intrhook)(sc, INTR_LOOP);#endif#if 1 /* * Interrupt ACK was posted asynchronously; wait for * completion here before reading SCB status again. * * If ACK fails, try to reset the chip, in hopes that * it helps. */ if (i82586_cmd_wait(sc) != 0) { reset = 1; break; }#endif IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); status = sc->ie_bus_read16(sc, off);#if I82586_DEBUG I82586_TRACE(sc, I82586_INTS_LOOPS, status);#endif } if (reset) {#if I82586_DEBUG printf("%s: intr reset; status=0x%x\n", sc->arpcom.ac_if.if_name, status);#endif i82586_cmd_wait(sc); i82586_reset(sc, 1); } #if I82586_DEBUG I82586_TRACE(sc, I82586_INTS_OUT, status);#endif if (sc->intrhook) (sc->intrhook)(sc, INTR_EXIT); }}/* * Process a received-frame interrupt. */inti82586_rint(struct ie_softc *sc, int scbstatus){ static int timesthru = 1024; int i, status, off;#if I82586_DEBUG I82586_TRACE(sc, I82586_RX_INT, scbstatus); if (sc->sc_debug & IED_RINT) printf("%s: rint: status 0x%x\n", sc->arpcom.ac_if.if_name, scbstatus);#endif for (;;) { int drop = 0; i = sc->rfhead; off = IE_RFRAME_STATUS(sc->rframes, i); IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); status = sc->ie_bus_read16(sc, off);#if I82586_DEBUG if (sc->sc_debug & IED_RINT) printf("%s: rint: frame(%d) status 0x%x\n", sc->arpcom.ac_if.if_name, i, status);#endif if ((status & IE_FD_COMPLETE) == 0) { if ((status & IE_FD_OK) != 0) { printf("%s: rint: weird: ", sc->arpcom.ac_if.if_name); i82586_rx_errors(sc, i, status);#if I82586_DEBUG I82586_TRACE(sc, I82586_RX_ERR, status);#endif break; } if (--timesthru == 0) { /* Account the accumulated errors */ i82586_count_errors(sc); timesthru = 1024; } break; } else if ((status & IE_FD_OK) == 0) { /* * If the chip is configured to automatically * discard bad frames, the only reason we can * get here is an "out-of-resource" condition. */ i82586_rx_errors(sc, i, status); drop = 1; #if I82586_DEBUG I82586_TRACE(sc, I82586_RX_DROP, status);#endif }#if I82586_DEBUG if ((status & IE_FD_BUSY) != 0) printf("%s: rint: frame(%d) busy; status=0x%x\n", sc->arpcom.ac_if.if_name, i, status);#endif /* * Advance the RFD list, since we're done with * this descriptor. */ /* Clear frame status */ sc->ie_bus_write16(sc, off, 0); /* Put fence at this frame (the head) */ off = IE_RFRAME_LAST(sc->rframes, i); sc->ie_bus_write16(sc, off, IE_FD_EOL|IE_FD_SUSP); /* and clear RBD field */ off = IE_RFRAME_BUFDESC(sc->rframes, i); sc->ie_bus_write16(sc, off, 0xffff); /* Remove fence from current tail */ off = IE_RFRAME_LAST(sc->rframes, sc->rftail); sc->ie_bus_write16(sc, off, 0); if (++sc->rftail == sc->nframes) sc->rftail = 0; if (++sc->rfhead == sc->nframes) sc->rfhead = 0; /* Pull the frame off the board */ if (drop) { i82586_drop_frames(sc); if ((status & IE_FD_RNR) != 0) sc->rnr_expect = 1; sc->arpcom.ac_if.if_ierrors++; } else if (ie_readframe(sc, i) != 0) return (1); } if ((scbstatus & IE_ST_RNR) != 0) { /* * Receiver went "Not Ready". We try to figure out * whether this was an expected event based on past * frame status values. */ if ((scbstatus & IE_RUS_SUSPEND) != 0) { /* * We use the "suspend on last frame" flag. * Send a RU RESUME command in response, since * we should have dealt with all completed frames * by now. */ printf("RINT: SUSPENDED; scbstatus=0x%x\n", scbstatus); if (i82586_start_cmd(sc, IE_RUC_RESUME, 0, 0, 0) == 0) return (0); printf("%s: RU RESUME command timed out\n", sc->arpcom.ac_if.if_name); return (1); /* Ask for a reset */ } if (sc->rnr_expect != 0) { /* * The RNR condition was announced in the previously * completed frame. Assume the receive ring is Ok, * so restart the receiver without further delay. */ i82586_start_transceiver(sc); sc->rnr_expect = 0; return (0); } else if ((scbstatus & IE_RUS_NOSPACE) != 0) { /* * We saw no previous IF_FD_RNR flag. * We check our ring invariants and, if ok, * just restart the receiver at the current * point in the ring. */ if (i82586_chk_rx_ring(sc) != 0) return (1); i82586_start_transceiver(sc); sc->arpcom.ac_if.if_ierrors++; return (0); } else printf("%s: receiver not ready; scbstatus=0x%x\n", sc->arpcom.ac_if.if_name, scbstatus); sc->arpcom.ac_if.if_ierrors++; return (1); /* Ask for a reset */ } return (0);}/* * Process a command-complete interrupt. These are only generated by the * transmission of frames. This routine is deceptively simple, since most * of the real work is done by i82586_start(). */inti82586_tint(struct ie_softc *sc, int scbstatus){ struct ifnet *ifp = &sc->arpcom.ac_if; int status;#if I82586_DEBUG I82586_TRACE(sc, I82586_TX_INT, sc->xmit_busy);#endif#if I82586_DEBUG if (sc->xmit_busy <= 0) { printf("i82586_tint: (%d), WEIRD: xmit_busy=%d, xctail=%d, xchead=%d\n", sc->trace_flow_in / 2, sc->xmit_busy, sc->xctail, sc->xchead); I82586_TRACE(sc, I82586_TX_BAD, sc->xctail); return (0); }#endif ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; status = sc->ie_bus_read16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds, sc->xctail));#if I82586_DEBUG if (sc->sc_debug & IED_TINT) printf("%s: tint: SCB status 0x%x; xmit status 0x%x\n", sc->arpcom.ac_if.if_name, scbstatus, status);#endif if ((status & IE_STAT_COMPL) == 0 || (status & IE_STAT_BUSY)) { printf("i82586_tint: (%d) command still busy; status=0x%x; tail=%d\n", sc->trace_flow_in / 2, status, sc->xctail); printf("iestatus = 0x%x\n", scbstatus);/* sc->sc_debug = IED_ALL; */ } if (status & IE_STAT_OK) { ifp->if_opackets++; ifp->if_collisions += (status & IE_XS_MAXCOLL); } else { ifp->if_oerrors++; /* * Check SQE and DEFERRED? * What if more than one bit is set? */ if (status & IE_STAT_ABORT) printf("%s: send aborted\n", sc->arpcom.ac_if.if_name); else if (status & IE_XS_NOCARRIER) printf("%s: no carrier\n", sc->arpcom.ac_if.if_name); else if (status & IE_XS_LOSTCTS) printf("%s: lost CTS\n", sc->arpcom.ac_if.if_name); else if (status & IE_XS_UNDERRUN) printf("%s: DMA underrun\n", sc->arpcom.ac_if.if_name); else if (status & IE_XS_EXCMAX) { printf("%s: too many collisions\n", sc->arpcom.ac_if.if_name); sc->arpcom.ac_if.if_collisions += 16; } } /* * If multicast addresses were added or deleted while transmitting, * ie_mc_reset() set the want_mcsetup flag indicating that we * should do it. */ if (sc->want_mcsetup) { ie_mc_setup(sc, IE_XBUF_ADDR(sc, sc->xctail)); sc->want_mcsetup = 0; } /* Done with the buffer. */ sc->xmit_busy--; sc->xctail = (sc->xctail + 1) % NTXBUF; /* Start the next packet, if any, transmitting. */ if (sc->xmit_busy > 0) iexmit(sc); i82586_start_tx(sc); return (0);}/* * Get a range of receive buffer descriptors that represent one packet.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -