📄 elnk.c
字号:
static void no_op(const rtems_irq_connect_data* irq){ return;}static int elnkIsOn(const rtems_irq_connect_data* irq){ return BSP_irq_enabled_at_i8259s (irq->name);}static void elnk_start_txchain( struct elnk_softc *sc, struct TXMD *chainhead ){ xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); /* save the address of the TX list */ sc->last_txchain_head = chainhead; sc->tx_idle = 0; xl_wait(sc); CSR_WRITE_4(sc, XL_DOWNLIST_PTR, phys_to_bus( sc->last_txchain_head )); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);}/* * ELNK interrupt handler */static rtems_isrelnk_interrupt_handler ( struct elnk_softc *sc ){ struct ifnet *ifp = &sc->arpcom.ac_if; u_int16_t status; while( ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS) && status != 0xFFFF) { sc->xl_stats.device_interrupts++; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK | (status & XL_INTRS));#if 0 printk("etherlink : unit elnk%d intstatus %04x\n", sc->xl_unit, status );#endif if (status & XL_STAT_UP_COMPLETE) {#if 0 printk("etherlink : unit elnk%d rx\n", sc->xl_unit );#endif /* received packets */ rtems_event_send(rxDaemonTid, sc->ioevent); } if( (status & XL_STAT_DOWN_COMPLETE) || (status & XL_STAT_TX_COMPLETE) ) { /* all packets uploaded to the device */ struct TXMD *chaintailmd = NULL; if( status & XL_STAT_TX_COMPLETE ) { /* if we got a tx complete error, count it, then reset the transmitter. Consider the entire chain lost.. */ ifp->if_oerrors++; sc->xl_stats.txcomplete_ints++; printk("etherlink : unit elnk%d transmit error\n", sc->xl_unit ); /* reset, re-enable fifo */ xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET | 1 ); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); xl_wait(sc); } /* send the chain head to the tx task which will recover the whole chain */ rtems_message_queue_send( chainRecoveryQueue, &sc->last_txchain_head, sizeof(struct TXMD *)); /* set up the next chain */ if( sc->last_txchain_head->chainptr ) { /* check the head of the chain of packets we just finished, * if != 0, either this is a chain of 2 or more packets or * its a single packet chain and another chain is ready to * send. */ if( (int)sc->last_txchain_head->chainptr == -1 ) { /* ** single packet was sent so no indirection to the last ** entry in the chain. since chainptr is != 0, then ** another chain is ready starting from the packet AFTER ** the chain we just finished. - in this case the last ** chain's head == its tail */ chaintailmd = sc->last_txchain_head; } else { /* ** otherwise, this is a pointer to the last packet in the ** chain of 2 or more packets. If the chain's last ** packet's chainptr is != 0, then another chain is ready ** to send. */ chaintailmd = sc->last_txchain_head->chainptr; if( !chaintailmd->chainptr ) chaintailmd = NULL; } } if( chaintailmd ) { /* the next MD is the start of another chain */ elnk_start_txchain(sc, chaintailmd->next_md ); } else { /* otherwise nothing to send, so go idle */ sc->tx_idle = -1; /* wake up the tx daemon once so we're sure this last chain will be freed */ rtems_event_send( txDaemonTid, sc->ioevent );#if 0 printk("unit elnk%d tx done\n", sc->xl_unit );#endif } } if (status & XL_STAT_ADFAIL) { printk("etherlink : unit elnk%d Catastrophic bus failure\n", sc->xl_unit ); } if (status & XL_STAT_STATSOFLOW) { sc->xl_stats_no_timeout = 1; xl_stats_update(sc->stat_timer_id,sc); sc->xl_stats_no_timeout = 0; } } #if 0 { unsigned16 intstatus, intenable, indenable; intstatus = CSR_READ_2(sc, XL_STATUS ); XL_SEL_WIN(5); intenable = CSR_READ_2(sc, XL_W5_INTR_ENB ); indenable = CSR_READ_2(sc, XL_W5_STAT_ENB ); XL_SEL_WIN(7); printk("etherlink : unit elnk%d istat %04x, ien %04x, ind %04x\n", sc->xl_unit, intstatus, intenable, indenable ); }#endif}static rtems_isrelnk_interrupt_handler_entry(){ int i; /* ** Check all the initialized units for interrupt service */ for(i=0; i< NUM_UNITS; i++ ) { if( elnk_softc[i].ioaddr ) elnk_interrupt_handler( &elnk_softc[i] ); }}/* * Initialize the ethernet hardware */static voidelnk_initialize_hardware (struct elnk_softc *sc){ unsigned char *cp; int i, j, rxsize, txsize, ringsize; /* * Init RX ring */ cp = (unsigned char *)malloc( (ringsize = ((rxsize = (sc->numRxbuffers * sizeof(struct RXMD))) + (txsize = (sc->numTxbuffers * sizeof(struct TXMD)))) ) + + CPU_CACHE_ALIGNMENT_FOR_BUFFER); sc->bufferBase = cp; cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1);#if defined(__i386__)#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA if (_CPU_is_paging_enabled()) _CPU_change_memory_mapping_attribute (NULL, cp, ringsize, PTE_CACHE_DISABLE | PTE_WRITABLE);#endif#endif sc->ringBase = cp; /* build tx and rx rings */ sc->rx_ring = (struct RXMD *)sc->ringBase; sc->tx_ring = (struct TXMD *)&sc->ringBase[ rxsize ]; { struct mbuf *m; struct RXMD *nxtmd; /* * The rx ring is easy as its just an array of RXMD structs. New * mbuf entries are allocated from the stack whenever the rx * daemon forwards an incoming packet into it. Here, we * pre-allocate the rx mbufs for the rx ring entries. */ for(i=0 ; i<sc->numRxbuffers; i++) { if( ((unsigned32)&sc->rx_ring[i] & 0x7) ) { rtems_panic ("etherlink : unit elnk%d rx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i ); } /* allocate an mbuf for each receive descriptor */ MGETHDR (m, M_WAIT, MT_DATA); MCLGET (m, M_WAIT); m->m_pkthdr.rcvif = &sc->arpcom.ac_if; if( i == sc->numRxbuffers-1 ) nxtmd = &sc->rx_ring[0]; else nxtmd = &sc->rx_ring[i+1]; sc->rx_ring[i].next_md = nxtmd; sc->rx_ring[i].mbuf = m; st_le32( &sc->rx_ring[i].status, 0); st_le32( &sc->rx_ring[i].next, (unsigned32)phys_to_bus( nxtmd )); st_le32( &sc->rx_ring[i].addr, (unsigned32)phys_to_bus( mtod(m, void *) )); st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE ); } sc->curr_rx_md = &sc->rx_ring[0]; } { struct TXMD *thismd, *nxtmd; /* * The tx ring is more complex. Each MD has an array of fragment * descriptors that are loaded from each packet as they arrive * from the stack. Each packet gets one ring entry, this allows * the lanboard to efficiently assemble the piecemeal packets into * a contiguous unit at transmit time, rather than spending * cputime concatenating them first. Although the next_md fields * form a ring, the DPD next is filled only when packets are added * to the tx chain, thus last entry of a series of packets has the * requisite dpd->next value == 0 to terminate the dma. mbuf * holds the packet info so it can be freed once the packet has * been sent. chainptr is used to link the head & tail of a chain * of 2 or more packets. A chain is formed when the tx daemon * gets 2 or more packets from the stack's queue in a service * period, so higher outgoing loads are handled as efficiently as * possible. */ for(i=0 ; i<sc->numTxbuffers; i++) { if( ((unsigned32)&sc->tx_ring[i] & 0x7) ) { rtems_panic ("etherlink : unit elnk%d tx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i ); } if( i == sc->numTxbuffers-1 ) nxtmd = &sc->tx_ring[0]; else nxtmd = &sc->tx_ring[i+1]; thismd = &sc->tx_ring[i]; thismd->next_md = nxtmd; thismd->chainptr = NULL; thismd->mbuf = NULL; st_le32( &thismd->status, XL_TXSTAT_DL_COMPLETE ); st_le32( &thismd->next, 0); for(j=0; j< NUM_FRAGS; j++) { st_le32( &thismd->txfrags[j].addr, 0 ); st_le32( &thismd->txfrags[j].length, 0 ); } } sc->last_tx_md = &sc->tx_ring[0]; }#ifdef ELNK_DEBUG printk("etherlink : %02x:%02x:%02x:%02x:%02x:%02x name 'elnk%d', io %x, int %d\n", sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1], sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3], sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5], sc->xl_unit, (unsigned)sc->ioaddr, sc->irqInfo.name );#endif sc->irqInfo.hdl = (rtems_irq_hdl)elnk_interrupt_handler_entry; sc->irqInfo.on = no_op; sc->irqInfo.off = no_op; sc->irqInfo.isOn = elnkIsOn; if( sc->irqInfo.name != 255 ) { int st;#ifdef BSP_SHARED_HANDLER_SUPPORT st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );#else st = BSP_install_rtems_irq_handler( &sc->irqInfo );#endif if (!st) rtems_panic ("etherlink : unit elnk%d Interrupt name %d already in use\n", sc->xl_unit, sc->irqInfo.name ); } else { printk("etherlink : unit elnk%d Interrupt not specified by device\n", sc->xl_unit ); }}static voidelnk_rxDaemon (void *arg){ struct elnk_softc *sc; struct ether_header *eh; struct mbuf *m; struct RXMD *rmd; unsigned int i,len, rxstat; rtems_event_set events; for (;;) { rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS, RTEMS_WAIT|RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events); for(;;) { for(i=0; i< NUM_UNITS; i++ ) { sc = &elnk_softc[i]; if( sc->ioaddr ) { if( events & sc->ioevent ) { struct ifnet *ifp = &sc->arpcom.ac_if; rmd = sc->curr_rx_md; /* ** Read off all the packets we've received on this unit */ while( (rxstat = ld_le32(&rmd->status)) ) { if (rxstat & XL_RXSTAT_UP_ERROR) { printk("unit %i up error\n", sc->xl_unit ); ifp->if_ierrors++; } if( (rxstat & XL_RXSTAT_UP_CMPLT) ) {#if 0 { char *pkt, *delim; int i; pkt = mtod(rmd->mbuf, char *); printk("unit %i rx pkt (%08x) ", sc->xl_unit, pkt ); for(delim="", i=0; i < sizeof(struct ether_header)+8; i++, delim=":") printk("%s%02x", delim, (char) pkt[i] ); printk("\n"); }#endif /* pass on the packet in the mbuf */ len = ( ld_le32(&rmd->status) & XL_RXSTAT_LENMASK); m = rmd->mbuf; m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header); eh = mtod(m, struct ether_header *); m->m_data += sizeof(struct ether_header); ether_input(ifp, eh, m); /* get a new mbuf */ MGETHDR (m, M_WAIT, MT_DATA); MCLGET (m, M_WAIT); m->m_pkthdr.rcvif = ifp; rmd->mbuf = m; st_le32( &rmd->status, 0 ); st_le32( &rmd->addr, (unsigned32)phys_to_bus(mtod(m, void *)) ); } else { /* some kind of packet failure */ printk("etherlink : unit elnk%d bad receive status -- packet dropped\n", sc->xl_unit); ifp->if_ierrors++; } /* clear descriptor status */ rmd->status = 0; rmd = rmd->next_md; } sc->curr_rx_md = rmd; } } } /* ** If more events are pending, service them befo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -