if_lan91cxx.c
字号:
p->tx_queue_len = 1; return 0; // OK }#endif default: break; } return 1;}//// This routine is called to see if it is possible to send another packet.// It will return non-zero if a transmit is possible, zero otherwise.//static intlan91cxx_can_send(struct eth_drv_sc *sc){ struct lan91cxx_priv_data *cpd = (struct lan91cxx_priv_data *)sc->driver_private; int tcr; DEBUG_FUNCTION();#ifndef LAN91CXX_IS_LAN91C111 // LINK_OK on 91C111 is just a general purpose input and may not // have anything to do with the link. if ((get_reg(sc, LAN91CXX_EPH_STATUS) & LAN91CXX_STATUS_LINK_OK) == 0) { db_printf("no link\n"); return false; // Link not connected }#endif CYG_ASSERT( cpd->within_send < 10, "can_send: Excess send recursions" ); cpd->within_send++; tcr = get_reg(sc, LAN91CXX_TCR); if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {#if DEBUG & 1 db_printf("%s: ENGINE RESTART: tcr %x\n", __FUNCTION__, tcr );#endif // Complete any outstanding activity: if ( cpd->txbusy ) { cpd->txbusy = 0;#if DEBUG & 9 db_printf("LAN91CXX - can_send, cleaning up pending TX\n" );#endif (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0); } tcr |= LAN91CXX_TCR_TXENA; put_reg(sc, LAN91CXX_TCR, tcr); } // This helps unstick deadly embraces. lan91cxx_poll( sc ); // Deal with any outstanding rx state cpd->within_send--; return (cpd->txbusy == 0) && (0 == cpd->within_send);}//// This routine is called to send data to the hardware.static void lan91cxx_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key){ struct lan91cxx_priv_data *cpd = (struct lan91cxx_priv_data *)sc->driver_private; int i, len, plen, tcr; unsigned short *sdata = NULL; unsigned short ints, control; cyg_uint16 packet, status; DEBUG_FUNCTION(); INCR_STAT( tx_count ); // Worry about the TX engine stopping. tcr = get_reg(sc, LAN91CXX_TCR); if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {#if DEBUG & 1 db_printf("%s: ENGINE RESTART: tcr %x\n", __FUNCTION__, tcr );#endif tcr |= LAN91CXX_TCR_TXENA; put_reg(sc, LAN91CXX_TCR, tcr); } // This helps unstick deadly embraces. CYG_ASSERT( cpd->within_send < 10, "send: Excess send recursions" ); cpd->within_send++; lan91cxx_poll( sc ); // Deal with any outstanding rx state cpd->within_send--; cpd->txbusy = 1; cpd->txkey = key; // Find packet length plen = 0; for (i = 0; i < sg_len; i++) plen += sg_list[i].len; CYG_ASSERT( plen == total_len, "sg data length mismatch" ); // Alloc new TX packet do { put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_alloc_for_tx#ifndef LAN91CXX_IS_LAN91C111 | ((plen >> 8) & 0x07)#endif ); i = 1024 * 1024; do { status = get_reg(sc, LAN91CXX_INTERRUPT); } while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT) && (--i > 0) ); if ( i ) packet = get_reg(sc, LAN91CXX_PNR); else packet = 0xffff;#if DEBUG & 1 db_printf("%s: allocated packet %04x\n", __FUNCTION__, packet);#endif packet = packet >> 8; if (packet & 0x80) { // Hm.. Isn't this a dead end?#if DEBUG & 1 db_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ );#endif // Not if we can make progress with what's filling memory. lan91cxx_poll( sc ); // Deal with any outstanding state continue; } } while (0);#if DEBUG & 4 db_printf("#####Tx packet allocated %x (previous %x)\n", packet, cpd->txpacket);#endif cpd->txpacket = packet; put_reg(sc, LAN91CXX_PNR, packet); // Note: Check FIFO state here before continuing? put_reg(sc, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000); // Pointer is now set, and the proper bank is selected for // data writes. // Prepare header: put_data(sc, CYG_CPU_TO_LE16(0)); // reserve space for status word // packet length (includes status, byte-count and control shorts) put_data(sc, CYG_CPU_TO_LE16(0x7FE & (plen + 6)) ); // Always even, always < 15xx(dec) // Put data into buffer for (i = 0; i < sg_len; i++) { sdata = (unsigned short *)sg_list[i].buf; len = sg_list[i].len; CYG_ASSERT(0 == (len & 1) || (i == (sg_len-1)), "odd length"); CYG_ASSERT( sdata, "No sg data pointer here" ); while(len >= sizeof(*sdata)) { put_data(sc, *sdata++); len -= sizeof(*sdata); } } CYG_ASSERT( sdata, "No sg data pointer outside" ); // Lay down the control short unconditionally at the end. // (or it might use random memory contents) control = 0; if ( 1 & plen ) { // Need to set ODD flag and insert the data unsigned char onebyte = *(unsigned char*)sdata; control = onebyte; control |= LAN91CXX_CONTROLBYTE_ODD; } control |= LAN91CXX_CONTROLBYTE_CRC; // Just in case... put_data(sc, CYG_CPU_TO_LE16(control)); // Enqueue the packet put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet); // Ack TX empty int and unmask it. ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00; put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_ACK); put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_M);#if DEBUG & 1 ints = get_reg(sc, LAN91CXX_INTERRUPT); db_printf("%s:END: ints at TX: %04x\n", __FUNCTION__, ints);#endif}static voidlan91cxx_TxEvent(struct eth_drv_sc *sc, int stat){ unsigned short packet, ints, tcr; struct lan91cxx_priv_data *cpd = (struct lan91cxx_priv_data *)sc->driver_private; int success = 1; DEBUG_FUNCTION(); INCR_STAT( tx_complete ); // Ack and mask TX interrupt set ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00; ints |= LAN91CXX_INTERRUPT_TX_SET_ACK; ints &= ~LAN91CXX_INTERRUPT_TX_SET_M; put_reg(sc, LAN91CXX_INTERRUPT, ints); // Get number of completed packet and read the status word packet = get_reg(sc, LAN91CXX_FIFO_PORTS);#if DEBUG & 1 db_printf("%s:START: fifo %04x ints %04x\n", __FUNCTION__, packet, ints);#endif#ifdef KEEP_STATISTICS { unsigned short reg; reg = get_reg( sc, LAN91CXX_EPH_STATUS ); // Covering each bit in turn... if ( reg & LAN91CXX_STATUS_TX_UNRN ) INCR_STAT( tx_underrun ); //if ( reg & LAN91CXX_STATUS_LINK_OK ) INCR_STAT( ); //if ( reg & LAN91CXX_STATUS_CTR_ROL ) INCR_STAT( ); //if ( reg & LAN91CXX_STATUS_EXC_DEF ) INCR_STAT( ); if ( reg & LAN91CXX_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss ); if ( reg & LAN91CXX_STATUS_LATCOL ) INCR_STAT( tx_late_collisions ); //if ( reg & LAN91CXX_STATUS_WAKEUP ) INCR_STAT( ); if ( reg & LAN91CXX_STATUS_TX_DEFR ) INCR_STAT( tx_deferred ); //if ( reg & LAN91CXX_STATUS_LTX_BRD ) INCR_STAT( ); if ( reg & LAN91CXX_STATUS_SQET ) INCR_STAT( tx_sqetesterrors ); if ( reg & LAN91CXX_STATUS_16COL ) INCR_STAT( tx_max_collisions ); //if ( reg & LAN91CXX_STATUS_LTX_MULT) INCR_STAT( ); if ( reg & LAN91CXX_STATUS_MUL_COL ) INCR_STAT( tx_mult_collisions ); if ( reg & LAN91CXX_STATUS_SNGL_COL ) INCR_STAT( tx_single_collisions ); if ( reg & LAN91CXX_STATUS_TX_SUC ) INCR_STAT( tx_good ); cpd->stats.tx_total_collisions = cpd->stats.tx_late_collisions + cpd->stats.tx_max_collisions + cpd->stats.tx_mult_collisions + cpd->stats.tx_single_collisions; // We do not need to look in the Counter Register (LAN91CXX_COUNTER) // because it just mimics the info we already have above. }#endif // KEEP_STATISTICS // We do not really care about Tx failure. Ethernet is not a reliable // medium. But we do care about the TX engine stopping. tcr = get_reg(sc, LAN91CXX_TCR); if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {#if DEBUG & 1 db_printf("%s: ENGINE RESTART: tcr %x ints %04x\n", __FUNCTION__, tcr, ints);#endif tcr |= LAN91CXX_TCR_TXENA; put_reg(sc, LAN91CXX_TCR, tcr); success = 0; // And treat this as an error... } packet &= 0xff; // It certainly appears that occasionally the tx fifo tells lies; we // get the wrong packet number. Freeing the one we allocated seems to // give correct operation.#ifdef CYGPKG_INFRA_DEBUG // Then we log, OOI, the number of times we get a bad packet number // from the tx done fifo. if (cpd->txpacket != packet ) lan91cxx_txfifo_bad++; else lan91cxx_txfifo_good++;#endif#if DEBUG & 4 db_printf("#####Tx packet freed %x (expected %x)\n", packet, cpd->txpacket );#endif // and then free the packet put_reg(sc, LAN91CXX_PNR, cpd->txpacket); put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet); // Ack the TX int which is supposed to clear the packet from the TX // completion queue. ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00; ints |= LAN91CXX_INTERRUPT_TX_FIFO_ACK; put_reg(sc, LAN91CXX_INTERRUPT, ints);#if DEBUG & 1 // Hm... The free doesn't seem to have the desired effect?!? ints = get_reg(sc, LAN91CXX_INTERRUPT); packet = get_reg(sc, LAN91CXX_FIFO_PORTS); db_printf("%s:END: fifo %04x ints %04x\n", __FUNCTION__, packet, ints);#endif if ( cpd->txbusy ) { cpd->txbusy = 0; (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success); }}//// This function is called when a packet has been received. Its job is// to prepare to unload the packet from the hardware. Once the length of// the packet is known, the upper layer of the driver can be told. When// the upper layer is ready to unload the packet, the internal function// 'lan91cxx_recv' will be called to actually fetch it from the hardware.//static voidlan91cxx_RxEvent(struct eth_drv_sc *sc){ struct lan91cxx_priv_data *cpd = (struct lan91cxx_priv_data *)sc->driver_private; unsigned short stat, len;#ifdef LAN91CXX_32BIT_RX cyg_uint32 val;#endif DEBUG_FUNCTION(); stat = get_reg(sc, LAN91CXX_FIFO_PORTS);#if DEBUG & 1 db_printf("RxEvent - FIFOs: 0x%04x\n", stat);#endif if ( 0x8000 & stat ) { // Then the Rx FIFO is empty#if DEBUG & 4 db_printf("#####RxEvent with empty fifo\n");#endif return; } INCR_STAT( rx_count );#if DEBUG & 4 db_printf("#####Rx packet allocated %x (previous %x)\n", 0xff & (stat >> 8), cpd->rxpacket );#endif // There is an Rx Packet ready cpd->rxpacket = 0xff & (stat >> 8); // Read status and (word) length put_reg(sc, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | LAN91CXX_POINTER_AUTO_INCR | 0x0000));#ifdef LAN91CXX_32BIT_RX val = get_data(sc); val = CYG_LE32_TO_CPU(val); stat = val & 0xffff; len = ((val >> 16) & 0xffff) - 6; // minus header/footer words#else stat = get_data(sc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -