/* * Au1x00 ethernet driver * * Copyright (c) 2005 by Cogent Computer Systems * Written by Jay Monkman * * The license and distribution terms for this file may be * found in found in the file LICENSE in this distribution or at * http://www.OARcorp.com/rtems/license.html. * * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_IFACES 1 #define NUM_TX_DMA_BUFS 4 #define NUM_RX_DMA_BUFS 4 /* RTEMS event used to start tx daemon. */ #define START_TX_EVENT RTEMS_EVENT_1 /* RTEMS event used to start rx daemon. */ #define START_RX_EVENT RTEMS_EVENT_2 rtems_isr au1x00_emac_isr(rtems_vector_number vector); #define TX_BUF_SIZE 2048 char tx_buf_base[(4 * TX_BUF_SIZE) + 32]; volatile int wait_count; /* * Hardware-specific storage */ typedef struct { /* * Connection to networking code * This entry *must* be the first in the sonic_softc structure. */ struct arpcom arpcom; /* * Interrupt vector */ rtems_vector_number vector; /* * Indicates configuration */ int acceptBroadcast; /* * Tasks waiting for interrupts */ rtems_id rx_daemon_tid; rtems_id tx_daemon_tid; /* * Buffers */ au1x00_macdma_rx_t *rx_dma; au1x00_macdma_tx_t *tx_dma; int rx_head; int rx_tail; int tx_head; int tx_tail; struct mbuf *rx_mbuf[NUM_RX_DMA_BUFS]; unsigned char *tx_buf[4]; /* * register addresses */ unsigned32 ctrl_regs; unsigned32 *en_reg; unsigned32 int_mask; unsigned32 int_ctrlr; /* * device */ int unitnumber; /* * Statistics */ unsigned long interrupts; unsigned long rx_interrupts; unsigned long tx_interrupts; unsigned long rx_missed; unsigned long rx_bcast; unsigned long rx_mcast; unsigned long rx_unsupp; unsigned long rx_ctrl; unsigned long rx_len_err; unsigned long rx_crc_err; unsigned long rx_dribble; unsigned long rx_mii_err; unsigned long rx_collision; unsigned long rx_too_long; unsigned long rx_runt; unsigned long rx_watchdog; unsigned long rx_pkts; unsigned long rx_dropped; unsigned long tx_deferred; unsigned long tx_underrun; unsigned long tx_aborted; unsigned long tx_pkts; } au1x00_emac_softc_t; static volatile au1x00_emac_softc_t softc[NUM_IFACES]; /* function prototypes */ int rtems_au1x00_emac_attach (struct rtems_bsdnet_ifconfig *config, int attaching); void au1x00_emac_init(void *arg); void au1x00_emac_init_hw(au1x00_emac_softc_t *sc); void au1x00_emac_start(struct ifnet *ifp); void au1x00_emac_stop (au1x00_emac_softc_t *sc); void au1x00_emac_tx_daemon (void *arg); void au1x00_emac_rx_daemon (void *arg); void au1x00_emac_sendpacket (struct ifnet *ifp, struct mbuf *m); void au1x00_emac_stats (au1x00_emac_softc_t *sc); static int au1x00_emac_ioctl (struct ifnet *ifp, int command, caddr_t data); static void mii_write(au1x00_emac_softc_t *sc, unsigned8 reg, unsigned16 val); static void mii_read(au1x00_emac_softc_t *sc, unsigned8 reg, unsigned16 *val); static void mii_init(au1x00_emac_softc_t *sc); static void mii_write(au1x00_emac_softc_t *sc, unsigned8 reg, unsigned16 val) { /* wait for the interface to get unbusy */ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) { continue; } /* write to address 0 - we only support address 0 */ AU1X00_MAC_MIIDATA(sc->ctrl_regs) = val; AU1X00_MAC_MIICTRL(sc->ctrl_regs) = (((reg & 0x1f) << 6) | AU1X00_MAC_MIICTRL_MW); au_sync(); /* wait for it to complete */ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) { continue; } } static void mii_read(au1x00_emac_softc_t *sc, unsigned8 reg, unsigned16 *val) { /* wait for the interface to get unbusy */ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) { continue; } /* write to address 0 - we only support address 0 */ AU1X00_MAC_MIICTRL(sc->ctrl_regs) = ((reg & 0x1f) << 6); au_sync(); /* wait for it to complete */ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) { continue; } *val = AU1X00_MAC_MIIDATA(sc->ctrl_regs); } static void mii_init(au1x00_emac_softc_t *sc) { unsigned16 data; mii_write(sc, 0, 0x8000); /* reset */ do { mii_read(sc, 0, &data); } while (data & 0x8000); mii_write(sc, 0, 0x3200); /* reset autonegotiation */ mii_write(sc, 17, 0xffc0); /* setup LEDs */ } int rtems_au1x00_emac_attach ( struct rtems_bsdnet_ifconfig *config, int attaching ) { struct ifnet *ifp; int mtu; int unitnumber; char *unitname; static au1x00_emac_softc_t *sc; /* * Parse driver name */ if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0) return 0; /* * Is driver free? */ if (unitnumber > NUM_IFACES) { printf ("Bad AU1X00 EMAC unit number.\n"); return 0; } sc = &softc[unitnumber]; ifp = &sc->arpcom.ac_if; if (ifp->if_softc != NULL) { printf ("Driver already in use.\n"); return 0; } /* * zero out the control structure */ memset(sc, 0, sizeof(*sc)); sc->unitnumber = unitnumber; sc->int_ctrlr = AU1X00_IC0_ADDR; if (unitnumber == 0) { sc->ctrl_regs = AU1100_MAC0_ADDR; sc->en_reg = (void *)(AU1100_MACEN_ADDR + 0); sc->tx_dma = (void *)(AU1X00_MACDMA0_ADDR + 0x000); sc->rx_dma = (void *)(AU1X00_MACDMA0_ADDR + 0x100); sc->int_mask = AU1X00_IC_IRQ_MAC0; } else { printk("Unknown network device: %d\n", unitnumber); return 0; } /* If the ethernet controller is already set up, read the MAC address */ if ((*sc->en_reg & 0x33) == 0x33) { sc->arpcom.ac_enaddr[5] = ((AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 8) & 0xff); sc->arpcom.ac_enaddr[4] = ((AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 0) & 0xff); sc->arpcom.ac_enaddr[3] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 24) & 0xff); sc->arpcom.ac_enaddr[2] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 16) & 0xff); sc->arpcom.ac_enaddr[1] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 8) & 0xff); sc->arpcom.ac_enaddr[0] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 0) & 0xff); } else { /* It's not set up yet, so we set a MAC address */ sc->arpcom.ac_enaddr[5] = 0x05; sc->arpcom.ac_enaddr[4] = 0xc0; sc->arpcom.ac_enaddr[3] = 0x50; sc->arpcom.ac_enaddr[2] = 0x31; sc->arpcom.ac_enaddr[1] = 0x23; sc->arpcom.ac_enaddr[0] = 0x00; } if (config->mtu) { mtu = config->mtu; } else { mtu = ETHERMTU; } sc->acceptBroadcast = !config->ignore_broadcast; /* * Set up network interface values */ ifp->if_softc = sc; ifp->if_unit = unitnumber; ifp->if_name = unitname; ifp->if_mtu = mtu; ifp->if_init = au1x00_emac_init; ifp->if_ioctl = au1x00_emac_ioctl; ifp->if_start = au1x00_emac_start; ifp->if_output = ether_output; ifp->if_flags = IFF_BROADCAST; if (ifp->if_snd.ifq_maxlen == 0) { ifp->if_snd.ifq_maxlen = ifqmaxlen; } /* * Attach the interface */ if_attach (ifp); ether_ifattach (ifp); return 1; } void au1x00_emac_init(void *arg) { au1x00_emac_softc_t *sc = arg; struct ifnet *ifp = &sc->arpcom.ac_if; /* *This is for stuff that only gets done once (au1x00_emac_init() * gets called multiple times */ if (sc->tx_daemon_tid == 0) { /* Set up EMAC hardware */ au1x00_emac_init_hw(sc); /* install the interrupt handler */ if (sc->unitnumber == 0) { set_vector(au1x00_emac_isr, AU1X00_IRQ_MAC0, 1); } else { set_vector(au1x00_emac_isr, AU1X00_IRQ_MAC1, 1); } AU1X00_IC_MASKCLR(sc->int_ctrlr) = sc->int_mask; au_sync(); /* set src bit */ AU1X00_IC_SRCSET(sc->int_ctrlr) = sc->int_mask; /* high level */ AU1X00_IC_CFG0SET(sc->int_ctrlr) = sc->int_mask; AU1X00_IC_CFG1CLR(sc->int_ctrlr) = sc->int_mask; AU1X00_IC_CFG2SET(sc->int_ctrlr) = sc->int_mask; /* assign to request 0 - negative logic */ AU1X00_IC_ASSIGNSET(sc->int_ctrlr) = sc->int_mask; au_sync(); /* Start driver tasks */ sc->tx_daemon_tid = rtems_bsdnet_newproc("ENTx", 4096, au1x00_emac_tx_daemon, sc); sc->rx_daemon_tid = rtems_bsdnet_newproc("ENRx", 4096, au1x00_emac_rx_daemon, sc); } /* EMAC doesn't support promiscuous, so ignore requests */ if (ifp->if_flags & IFF_PROMISC) printf ("Warning - AU1X00 EMAC doesn't support Promiscuous Mode!\n"); /* * Tell the world that we're running. */ ifp->if_flags |= IFF_RUNNING; /* * start tx, rx */ AU1X00_MAC_CONTROL(sc->ctrl_regs) |= (AU1X00_MAC_CTRL_TE | AU1X00_MAC_CTRL_RE); au_sync(); } /* au1x00_emac_init() */ void au1x00_emac_init_hw(au1x00_emac_softc_t *sc) { int i; struct mbuf *m; struct ifnet *ifp = &sc->arpcom.ac_if; /* reset the MAC */ *sc->en_reg = 0x40; au_sync(); for (i = 0; i < 10000; i++) { continue; } /* *sc->en_reg = AU1X00_MAC_EN_CE; */ *sc->en_reg = 41; au_sync(); for (i = 0; i < 10000; i++) { continue; } /* *sc->en_reg = (AU1X00_MAC_EN_CE | AU1X00_MAC_EN_E2 | AU1X00_MAC_EN_E1 | AU1X00_MAC_EN_E0); */ *sc->en_reg = 0x33; au_sync(); mii_init(sc); /* set the mac address */ AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) = ((sc->arpcom.ac_enaddr[5] << 8) | (sc->arpcom.ac_enaddr[4] << 0)); AU1X00_MAC_ADDRLOW(sc->ctrl_regs) = ((sc->arpcom.ac_enaddr[3] << 24) | (sc->arpcom.ac_enaddr[2] << 16) | (sc->arpcom.ac_enaddr[1] << 8) | (sc->arpcom.ac_enaddr[0] << 0)); /* get the MAC address from the chip */ sc->arpcom.ac_enaddr[5] = (AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 8) & 0xff; sc->arpcom.ac_enaddr[4] = (AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 0) & 0xff; sc->arpcom.ac_enaddr[3] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 24) & 0xff; sc->arpcom.ac_enaddr[2] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 16) & 0xff; sc->arpcom.ac_enaddr[1] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 8) & 0xff; sc->arpcom.ac_enaddr[0] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 0) & 0xff; printk("Setting mac_control to 0x%x\n", (AU1X00_MAC_CTRL_F | AU1X00_MAC_CTRL_PM | AU1X00_MAC_CTRL_RA | AU1X00_MAC_CTRL_DO | AU1X00_MAC_CTRL_EM)); AU1X00_MAC_CONTROL(sc->ctrl_regs) = (AU1X00_MAC_CTRL_F | /* full duplex */ AU1X00_MAC_CTRL_PM | /* pass mcast */ AU1X00_MAC_CTRL_RA | /* recv all */ AU1X00_MAC_CTRL_DO | /* disable own */ AU1X00_MAC_CTRL_EM); /* Big endian */ au_sync(); printk("mac_control was set to 0x%x\n", AU1X00_MAC_CONTROL(sc->ctrl_regs)); printk("mac_control addr is 0x%x\n", &AU1X00_MAC_CONTROL(sc->ctrl_regs)); /* initialize our receive buffer descriptors */ for (i = 0; i < NUM_RX_DMA_BUFS; i++) { MGETHDR(m, M_WAIT, MT_DATA); MCLGET(m, M_WAIT); m->m_pkthdr.rcvif = ifp; m->m_nextpkt = 0; /* * The receive buffer must be aligned with a cache line * boundary. */ if (mtod(m, unsigned32) & 0x1f) { unsigned32 *p = &mtod(m, unsigned32); *p = (mtod(m, unsigned32) + 0x1f) & 0x1f; } sc->rx_dma[i].addr = (mtod(m, unsigned32) & ~0xe0000000); sc->rx_mbuf[i] = m; } /* Initialize transmit buffer descriptors */ for (i = 0; i < NUM_TX_DMA_BUFS; i++) { sc->tx_dma[i].addr = 0; } /* initialize the transmit buffers */ sc->tx_buf[0] = (void *)((((int)&tx_buf_base[0]) + 0x1f) & ~0x1f); sc->tx_buf[1] = (void *)(((int)sc->tx_buf[0]) + TX_BUF_SIZE); sc->tx_buf[2] = (void *)(((int)sc->tx_buf[1]) + TX_BUF_SIZE); sc->tx_buf[3] = (void *)(((int)sc->tx_buf[2]) + TX_BUF_SIZE); sc->rx_head = (sc->rx_dma[0].addr >> 2) & 0x3; sc->rx_tail = (sc->rx_dma[0].addr >> 2) & 0x3; sc->tx_head = (sc->tx_dma[0].addr >> 2) & 0x3; sc->tx_tail = (sc->tx_dma[0].addr >> 2) & 0x3; for (i = 0; i < NUM_RX_DMA_BUFS; i++) { sc->rx_dma[i].addr |= AU1X00_MAC_DMA_RXADDR_EN; } } /* au1x00_emac_init_hw() */ void au1x00_emac_start(struct ifnet *ifp) { au1x00_emac_softc_t *sc = ifp->if_softc; rtems_event_send(sc->tx_daemon_tid, START_TX_EVENT); ifp->if_flags |= IFF_OACTIVE; } void au1x00_emac_stop (au1x00_emac_softc_t *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; ifp->if_flags &= ~IFF_RUNNING; /* * Stop the transmitter and receiver. */ /* Disable TX/RX */ AU1X00_MAC_CONTROL(sc->ctrl_regs) &= ~(AU1X00_MAC_CTRL_TE | AU1X00_MAC_CTRL_RE); au_sync(); } /* * Driver tx daemon */ void au1x00_emac_tx_daemon (void *arg) { au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)arg; struct ifnet *ifp = &sc->arpcom.ac_if; struct mbuf *m; rtems_event_set events; unsigned32 ic_base; /* interrupt controller */ ic_base = AU1X00_IC0_ADDR; /* turn on interrupt, then wait for one */ if (sc->unitnumber == 0) { AU1X00_IC_MASKSET(ic_base) = AU1X00_IC_IRQ_MAC0; } else { AU1X00_IC_MASKSET(ic_base) = AU1X00_IC_IRQ_MAC1; } au_sync(); for (;;) { rtems_bsdnet_event_receive( START_TX_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); /* Send packets till queue is empty */ for (;;) { /* Get the next mbuf chain to transmit. */ IF_DEQUEUE(&ifp->if_snd, m); if (!m) break; sc->tx_pkts++; au1x00_emac_sendpacket (ifp, m); } ifp->if_flags &= ~IFF_OACTIVE; } } /* * Driver rx daemon */ void au1x00_emac_rx_daemon (void *arg) { au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)arg; struct ifnet *ifp = &sc->arpcom.ac_if; struct mbuf *m; struct ether_header *eh; rtems_event_set events; unsigned32 status; while (1) { rtems_bsdnet_event_receive( START_RX_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); /* while there are packets to receive */ while (!(sc->rx_dma[sc->rx_head].addr & (AU1X00_MAC_DMA_RXADDR_DN | AU1X00_MAC_DMA_RXADDR_EN))) { status = sc->rx_dma[sc->rx_head].stat; if (status & AU1X00_MAC_DMA_RXSTAT_MI) { sc->rx_missed++; } if (status & AU1X00_MAC_DMA_RXSTAT_BF) { sc->rx_bcast++; } if (status & AU1X00_MAC_DMA_RXSTAT_MF) { sc->rx_mcast++; } if (status & AU1X00_MAC_DMA_RXSTAT_UC) { sc->rx_unsupp++; } if (status & AU1X00_MAC_DMA_RXSTAT_CF) { sc->rx_ctrl++; } if (status & AU1X00_MAC_DMA_RXSTAT_LE) { sc->rx_len_err++; } if (status & AU1X00_MAC_DMA_RXSTAT_CR) { sc->rx_crc_err++; } if (status & AU1X00_MAC_DMA_RXSTAT_DB) { sc->rx_dribble++; } if (status & AU1X00_MAC_DMA_RXSTAT_ME) { sc->rx_mii_err++; } if (status & AU1X00_MAC_DMA_RXSTAT_CS) { sc->rx_collision++; } if (status & AU1X00_MAC_DMA_RXSTAT_FL) { sc->rx_too_long++; } if (status & AU1X00_MAC_DMA_RXSTAT_RF) { sc->rx_runt++; } if (status & AU1X00_MAC_DMA_RXSTAT_WT) { sc->rx_watchdog++; } /* If no errrors, accept packet */ if ((status & (AU1X00_MAC_DMA_RXSTAT_CR | AU1X00_MAC_DMA_RXSTAT_DB | AU1X00_MAC_DMA_RXSTAT_RF)) == 0) { sc->rx_pkts++; /* find the start of the mbuf */ m = sc->rx_mbuf[sc->rx_head]; /* set the length of the mbuf */ m->m_len = AU1X00_MAC_DMA_RXSTAT_LEN(sc->rx_dma[sc->rx_head].stat); m->m_len -= 4; /* remove ethernet CRC */ m->m_pkthdr.len = m->m_len; /* strip off the ethernet header from the mbuf */ /* but save the pointer to it */ eh = mtod (m, struct ether_header *); m->m_data += sizeof(struct ether_header); /* give the received packet to the stack */ ether_input(ifp, eh, m); /* get a new buf and make it ready for the MAC */ MGETHDR(m, M_WAIT, MT_DATA); MCLGET(m, M_WAIT); m->m_pkthdr.rcvif = ifp; m->m_nextpkt = 0; /* * The receive buffer must be aligned with a cache line * boundary. */ { unsigned32 *p = &mtod(m, unsigned32); *p = (mtod(m, unsigned32) + 0x1f) & ~0x1f; } } else { sc->rx_dropped++; /* find the mbuf so we can reuse it*/ m = sc->rx_mbuf[sc->rx_head]; } /* set up the receive dma to use the mbuf's cluster */ sc->rx_dma[sc->rx_head].addr = (mtod(m, unsigned32) & ~0xe0000000); au_sync(); sc->rx_mbuf[sc->rx_head] = m; sc->rx_dma[sc->rx_head].addr |= AU1X00_MAC_DMA_RXADDR_EN; au_sync(); /* increment the buffer index */ sc->rx_head++; if (sc->rx_head >= NUM_RX_DMA_BUFS) { sc->rx_head = 0; } } } } /* Send packet */ void au1x00_emac_sendpacket (struct ifnet *ifp, struct mbuf *m) { struct mbuf *l = NULL; unsigned int pkt_offset = 0; au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)ifp->if_softc; unsigned32 txbuf; /* Wait for EMAC Transmit Queue to become available. */ while((sc->tx_dma[sc->tx_head].addr & (AU1X00_MAC_DMA_TXADDR_EN || AU1X00_MAC_DMA_TXADDR_DN)) != 0) { continue; } /* copy the mbuf chain into the transmit buffer */ l = m; txbuf = (unsigned32)sc->tx_buf[sc->tx_head]; while (l != NULL) { memcpy(((char *)txbuf + pkt_offset), /* offset into pkt for mbuf */ (char *)mtod(l, void *), /* cast to void */ l->m_len); /* length of this mbuf */ pkt_offset += l->m_len; /* update offset */ l = l->m_next; /* get next mbuf, if any */ } /* Pad if necessary */ if (pkt_offset < 60) { memset((char *)(txbuf + pkt_offset), 0, (60 - pkt_offset)); pkt_offset = 60; } /* send it off */ sc->tx_dma[sc->tx_head].stat = 0; sc->tx_dma[sc->tx_head].len = pkt_offset; sc->tx_dma[sc->tx_head].addr = ((txbuf & ~0xe0000000) | AU1X00_MAC_DMA_TXADDR_EN); au_sync(); /* *Without this delay, some outgoing packets never * make it out the device. Nothing in the documentation * explains this. */ for (wait_count = 0; wait_count < 5000; wait_count++){ continue; } /* free the mbuf chain we just copied */ m_freem(m); sc->tx_head++; if (sc->tx_head >= NUM_TX_DMA_BUFS) { sc->tx_head = 0; } } /* au1x00_emac_sendpacket () */ /* Show interface statistics */ void au1x00_emac_stats (au1x00_emac_softc_t *sc) { printf("Interrupts:%-8lu", sc->interrupts); printf(" RX Interrupts:%-8lu", sc->rx_interrupts); printf(" TX Interrupts:%-8lu\n", sc->tx_interrupts); printf("RX Packets:%-8lu", sc->rx_pkts); printf(" RX Control:%-8lu", sc->rx_ctrl); printf(" RX broadcast:%-8lu\n", sc->rx_bcast); printf("RX Mcast:%-8lu", sc->rx_mcast); printf(" RX missed:%-8lu", sc->rx_missed); printf(" RX Unsupported ctrl:%-8lu\n", sc->rx_unsupp); printf("RX Len err:%-8lu", sc->rx_len_err); printf(" RX CRC err:%-8lu", sc->rx_crc_err); printf(" RX dribble:%-8lu\n", sc->rx_dribble); printf("RX MII err:%-8lu", sc->rx_mii_err); printf(" RX collision:%-8lu", sc->rx_collision); printf(" RX too long:%-8lu\n", sc->rx_too_long); printf("RX runt:%-8lu", sc->rx_runt); printf(" RX watchdog:%-8lu", sc->rx_watchdog); printf(" RX dropped:%-8lu\n", sc->rx_dropped); printf("TX Packets:%-8lu", sc->tx_pkts); printf(" TX Deferred:%-8lu", sc->tx_deferred); printf(" TX Underrun:%-8lu\n", sc->tx_underrun); printf("TX Aborted:%-8lu\n", sc->tx_aborted); } /* Driver ioctl handler */ static int au1x00_emac_ioctl (struct ifnet *ifp, int command, caddr_t data) { au1x00_emac_softc_t *sc = ifp->if_softc; int error = 0; switch (command) { case SIOCGIFADDR: case SIOCSIFADDR: ether_ioctl (ifp, command, data); break; case SIOCSIFFLAGS: switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { case IFF_RUNNING: au1x00_emac_stop (sc); break; case IFF_UP: au1x00_emac_init (sc); break; case IFF_UP | IFF_RUNNING: au1x00_emac_stop (sc); au1x00_emac_init (sc); break; default: break; } /* switch (if_flags) */ break; case SIO_RTEMS_SHOW_STATS: au1x00_emac_stats (sc); break; /* * FIXME: All sorts of multicast commands need to be added here! */ default: error = EINVAL; break; } /* switch (command) */ return error; } /* interrupt handler */ rtems_isr au1x00_emac_isr (rtems_vector_number v) { volatile au1x00_emac_softc_t *sc; int index; int tx_flag = 0; int rx_flag = 0; if (v == AU1X00_IRQ_MAC0) { sc = &softc[0]; } else { assert(v == AU1X00_IRQ_MAC0); } sc->interrupts++; /* * Since there's no easy way to find out the source of the * interrupt, we have to look at the tx and rx dma buffers */ /* receive interrupt */ while(sc->rx_dma[sc->rx_tail].addr & AU1X00_MAC_DMA_RXADDR_DN) { rx_flag = 1; sc->rx_interrupts++; sc->rx_dma[sc->rx_tail].addr &= ~AU1X00_MAC_DMA_RXADDR_DN; au_sync(); sc->rx_tail++; if (sc->rx_tail >= NUM_RX_DMA_BUFS) { sc->rx_tail = 0; } } if (rx_flag != 0) { rtems_event_send(sc->rx_daemon_tid, START_RX_EVENT); } /* transmit interrupt */ while (sc->tx_dma[sc->tx_tail].addr & AU1X00_MAC_DMA_TXADDR_DN) { unsigned32 status; tx_flag = 1; sc->tx_interrupts++; status = sc->tx_dma[sc->tx_tail].stat; if (status & AU1X00_MAC_DMA_TXSTAT_DF) { sc->tx_deferred++; } if (status & AU1X00_MAC_DMA_TXSTAT_UR) { sc->tx_underrun++; } if (status & AU1X00_MAC_DMA_TXSTAT_FA) { sc->tx_aborted++; } sc->tx_dma[sc->tx_tail].addr = 0; au_sync(); sc->tx_tail++; if (sc->tx_tail >= NUM_TX_DMA_BUFS) { sc->tx_tail = 0; } } if (tx_flag != 0) { rtems_event_send(sc->tx_daemon_tid, START_TX_EVENT); } }