diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 09:53:31 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:44 +0200 |
commit | 031df3914990db0336a0d386fb53558b05de467e (patch) | |
tree | 4661e22f0cdb3f9d06879f0194b77c75f62bac79 /bsps/powerpc/psim/net/if_sim.c | |
parent | bsps: Move interrupt controller support to bsps (diff) | |
download | rtems-031df3914990db0336a0d386fb53558b05de467e.tar.bz2 |
bsps: Move legacy network drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/powerpc/psim/net/if_sim.c')
-rw-r--r-- | bsps/powerpc/psim/net/if_sim.c | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/bsps/powerpc/psim/net/if_sim.c b/bsps/powerpc/psim/net/if_sim.c new file mode 100644 index 0000000000..3bb1ec362d --- /dev/null +++ b/bsps/powerpc/psim/net/if_sim.c @@ -0,0 +1,504 @@ +/* Trivial driver for PSIM's emulated ethernet device 'hw_ethtap'. + * + * NOTE: At the time of this writing (2009/1) 'hw_ethtap' requires + * a patched version of PSIM -- the vanilla version does not + * implement this device. + * Also, support for this device is currently only available + * on a linux host. + * + * Author/Copyright: Till Straumann <Till.Straumann@TU-Berlin.de> + * + * LICENSE + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + */ + +#include <bsp.h> +#include <rtems.h> +#include <bsp/irq.h> +#include <psim.h> +#include <libcpu/io.h> +#include <inttypes.h> + + +#ifndef KERNEL +#define KERNEL +#endif +#ifndef _KERNEL +#define _KERNEL +#endif + +#include <rtems/rtems_bsdnet.h> +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <stdio.h> + +#define IFSIM_SLOTS 1 + +#define DRVNAME "if_sim" + +#define IFSIM_TX_BUF_REG 1 +#define IFSIM_TX_STA_REG 2 + +#define IFSIM_RX_BUF_REG 4 +#define IFSIM_RX_STA_REG 5 + +#define IFSIM_RX_CNT_MSK 0xffff +#define IFSIM_RX_STA_DONE (1<<31) + +#define IFSIM_RX_ERR_NOSPC (1<<20) /* buffer too small */ +#define IFSIM_RX_ERR_DMA (1<<21) /* DMA error */ +#define IFSIM_RX_ERR_RD (1<<23) /* file read error */ + +#define IFSIM_TX_STA_LST (1<<16) +#define IFSIM_TX_STA_DONE (1<<31) + +#define IFSIM_IEN_REG 6 +#define IFSIM_IRQ_REG 7 + +#define IFSIM_RX_IRQ (1<<0) + +#define IFSIM_MACA0_REG 8 +#define IFSIM_MACA1_REG 9 + +#define IFSIM_CSR_REG 10 +#define IFSIM_CSR_PROM (1<<0) +/* Enable CRC generation/checking */ +#define IFSIM_CSR_CRCEN (1<<1) + + +#define RX_BUF_ALIGNMENT 1 +#define ETH_RX_OFFSET 0 + +/* + * Align 'p' up to a multiple of 'a' which must be + * a power of two. Result is cast to (uintptr_t) + */ +#define ALIGNTO(p,a) ((((uintptr_t)(p)) + (a) - 1) & ~((a)-1)) + + +typedef volatile unsigned reg_t; + +struct ifsim_private { + reg_t *base; + void *rbuf; + unsigned rx_cserrs; + unsigned rx_err_nospc; + unsigned rx_err_dma; + unsigned rx_err_rd; + unsigned rx_nobufs; + unsigned rx_irqs; +}; + +struct ifsim_softc { + struct arpcom arpcom; + struct ifsim_private pvt; +}; + +struct ifsim_softc theIfSims[IFSIM_SLOTS] = {{{{0}}} }; + +rtems_id ifsim_tid = 0; + +static __inline__ uint32_t +ifsim_in(struct ifsim_softc *sc, unsigned regno) +{ + return in_be32((volatile uint32_t *) (sc->pvt.base + regno)); +} + +static __inline__ void +ifsim_out(struct ifsim_softc *sc, unsigned regno, uint32_t v) +{ + out_be32((volatile uint32_t *) (sc->pvt.base + regno), v); +} + +static void * +alloc_mbuf_rx(int *psz, uintptr_t *paddr) +{ +struct mbuf *m; +unsigned long l,o; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if ( !m ) + return 0; + MCLGET(m, M_DONTWAIT); + if ( ! (m->m_flags & M_EXT) ) { + m_freem(m); + return 0; + } + + o = mtod(m, unsigned long); + l = ALIGNTO(o, RX_BUF_ALIGNMENT) - o; + + /* align start of buffer */ + m->m_data += l; + + /* reduced length */ + l = MCLBYTES - l; + + m->m_len = m->m_pkthdr.len = l; + *psz = m->m_len; + *paddr = mtod(m, unsigned long); + + return (void*) m; +} + +static int +get_rxbuf(struct ifsim_softc *sc) +{ +int sz; +uintptr_t addr; +void *nbuf; + + nbuf = alloc_mbuf_rx(&sz, &addr); + + if ( nbuf ) { + sc->pvt.rbuf = nbuf; + ifsim_out(sc, IFSIM_RX_BUF_REG, addr); + ifsim_out(sc, IFSIM_RX_STA_REG, sz); + return 0; + } + return -1; +} + +/* set/clear promiscuous mode */ +static void +ifsim_upd_promisc(struct ifsim_softc *sc) +{ +uint32_t ncsr, csr; + + ncsr = csr = ifsim_in(sc, IFSIM_CSR_REG); + + if ( sc->arpcom.ac_if.if_flags & IFF_PROMISC ) + ncsr |= IFSIM_CSR_PROM; + else + ncsr &= ~IFSIM_CSR_PROM; + + if ( ncsr != csr ) + ifsim_out(sc, IFSIM_CSR_REG, ncsr); +} + +static void +ifsim_init(void *arg) +{ +struct ifsim_softc *sc = arg; +struct ifnet *ifp = &sc->arpcom.ac_if; + + if ( 0 == get_rxbuf(sc) ) { + ifsim_upd_promisc(sc); + ifp->if_flags |= IFF_RUNNING; + } +} + +static void +ifsim_start(struct ifnet *ifp) +{ +struct ifsim_softc *sc = ifp->if_softc; +struct mbuf *m, *mh, *m1; + + while ( ifp->if_snd.ifq_head ) { + IF_DEQUEUE( &ifp->if_snd, mh ); + for ( m=mh, m1 = m->m_next ; m1 ; m1 = m1->m_next ) { + ifsim_out(sc, IFSIM_TX_BUF_REG, mtod(m, uint32_t)); + ifsim_out(sc, IFSIM_TX_STA_REG, m->m_len); + /* dummy-busywait; the emulated hardware DMAs our + * data away 'immediately' i.e., this loop is + * never executed + */ + while ( ! (IFSIM_TX_STA_DONE & ifsim_in(sc, IFSIM_TX_STA_REG)) ) + /* Loop */; + m = m1; + } + ifsim_out(sc, IFSIM_TX_BUF_REG, mtod(m, uint32_t)); + ifsim_out(sc, IFSIM_TX_STA_REG, m->m_len | IFSIM_TX_STA_LST); + + /* dummy-busywait; the emulated hardware DMAs our + * data away 'immediately' i.e., this loop is + * never executed + */ + while ( ! (IFSIM_TX_STA_DONE & ifsim_in(sc, IFSIM_TX_STA_REG)) ) + /* Loop */; + + m_freem(mh); + } +} + +static int +ifsim_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ +struct ifsim_softc *sc = ifp->if_softc; +int rval = 0; +int f; + + switch (cmd) { + + case SIOCSIFFLAGS: + f = ifp->if_flags; + if ( f & IFF_UP ) { + if ( ! (f & IFF_RUNNING) ) { + ifsim_init(sc); + } else { + ifsim_upd_promisc(sc); + } + /* FIXME: handle other flags here */ + } else { + if ( f & IFF_RUNNING ) { + printk("WARNING: bringing "DRVNAME" down not really implemented\n"); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + } + } + + break; + + case SIO_RTEMS_SHOW_STATS: + printf("RX bad chksum : %u\n", sc->pvt.rx_cserrs); + printf("RX no space : %u\n", sc->pvt.rx_err_nospc); + printf("RX bad DMA : %u\n", sc->pvt.rx_err_dma); + printf("RX read errors : %u\n", sc->pvt.rx_err_rd); + printf("rx_nobufs : %u\n", sc->pvt.rx_nobufs); + printf("rx_irqs : %u\n", sc->pvt.rx_irqs); + break; + + default: + rval = ether_ioctl(ifp, cmd, data); + break; + } + + return rval; +} + +static void ifsim_irq_on(const rtems_irq_connect_data *pd) +{ +struct ifsim_softc *sc = pd->handle; + ifsim_out(sc, IFSIM_IEN_REG, IFSIM_RX_IRQ); +} + +static void ifsim_irq_off(const rtems_irq_connect_data *pd) +{ +struct ifsim_softc *sc = pd->handle; + ifsim_out(sc, IFSIM_IEN_REG, 0); +} + +static int ifsim_irq_ison(const rtems_irq_connect_data *pd) +{ +struct ifsim_softc *sc = pd->handle; + return ifsim_in(sc, IFSIM_IEN_REG) & IFSIM_RX_IRQ ? 1 : 0; +} + +static void +ifsim_isr(void *arg) +{ +struct ifsim_softc *sc = arg; + + sc->pvt.rx_irqs++; +#ifdef IRQ_DEBUG + printk("ISR happened\n"); +#endif + + ifsim_out(sc, IFSIM_IEN_REG, 0); + rtems_bsdnet_event_send(ifsim_tid, (1<<(sc-theIfSims))); +} + +static void +ifsim_task(void *arg) +{ +struct ifsim_softc *sc; +uint32_t sta; +struct ifnet *ifp; +unsigned len; +rtems_event_set evs; + + while (1) { + + rtems_bsdnet_event_receive( + ((1<<IFSIM_SLOTS)-1), + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &evs); + + evs &= ((1<<IFSIM_SLOTS)-1); + +#ifdef IRQ_DEBUG + printk("Task got evs %u\n", evs); +#endif + + for ( sc = theIfSims; evs; evs>>=1, sc++ ) { + + if ( ! ( evs & 1 ) ) + continue; + + ifp = &sc->arpcom.ac_if; + + while ( ifsim_in(sc, IFSIM_IRQ_REG) & IFSIM_RX_IRQ ) { + struct mbuf *m = sc->pvt.rbuf; + + sta = ifsim_in(sc, IFSIM_RX_STA_REG); + + + if ( (sta & IFSIM_RX_STA_DONE) ) { + + if ( (ifp->if_flags & IFF_RUNNING) ) { + if ( 0 == get_rxbuf(sc) ) { + /* enqueue packet */ + struct ether_header *eh; + uint32_t crc_net, crc; + int crc_len; + + crc_len = (IFSIM_CSR_CRCEN & ifsim_in(sc, IFSIM_CSR_REG)) ? sizeof(crc_net) : 0; + + len = (sta & IFSIM_RX_CNT_MSK) - crc_len; + + eh = (struct ether_header*)(mtod(m,unsigned long) + ETH_RX_OFFSET); + + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header) - ETH_RX_OFFSET; + m->m_data += sizeof(struct ether_header) + ETH_RX_OFFSET; + m->m_pkthdr.rcvif = ifp; + +#ifdef DEBUG_WO_BSDNET + { + int i; + for ( i=0; i<len + crc_len; ) { + printk("%02X ",((char*)eh)[i]); + if ( 0 == (++i & 0xf) ) + fputc('\n',stdout); + } + if ( i & 0xf ) + fputc('\n', stdout); + printk("*****\n"); + } +#endif + + if ( crc_len + && (memcpy(&crc_net, (char*)eh + len, crc_len), + (crc = (ether_crc32_le((uint8_t *)eh, len) ^ 0xffffffff)) != crc_net) ) { + printk("CSUM: me 0x%08" PRIx32 ", them 0x%08" PRIx32 "\n", crc, crc_net); + sc->pvt.rx_cserrs++; + } else { + + ifp->if_ipackets++; + ifp->if_ibytes += len; + +#ifdef DEBUG_WO_BSDNET + m_freem(m); +#else + ether_input(ifp, eh, m); +#endif + + m = 0; + } + + } else { + /* throw away and reuse buffer */ + sc->pvt.rx_nobufs++; + } + } + } else { + /* throw away and reuse buffer */ + if ( (sta & IFSIM_RX_ERR_NOSPC) ) + sc->pvt.rx_err_nospc++; + if ( (sta & IFSIM_RX_ERR_DMA) ) + sc->pvt.rx_err_dma++; + if ( (sta & IFSIM_RX_ERR_RD) ) + sc->pvt.rx_err_rd++; + } + + if ( m ) { + /* reuse */ + ifsim_out(sc, IFSIM_RX_STA_REG, m->m_pkthdr.len); + } + } + /* re-enable interrupt */ + ifsim_out(sc, IFSIM_IEN_REG, IFSIM_RX_IRQ); + } + } +} + +int +rtems_ifsim_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ +char *unitName; +int unit; +struct ifsim_softc *sc; +struct ifnet *ifp; +uint32_t v; +rtems_irq_connect_data ihdl; + + if ( !attaching ) + return -1; + + unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName); + + if ( unit <= 0 || unit > IFSIM_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, IFSIM_SLOTS); + return 1; + } + + sc = &theIfSims[unit-1]; + ifp = &sc->arpcom.ac_if; + + memset(&sc->pvt, 0, sizeof(sc->pvt)); + + sc->pvt.base = (reg_t*)ifcfg->port; + + if ( 0 == sc->pvt.base ) + sc->pvt.base = (reg_t*)PSIM.Ethtap; + + + sc->pvt.rbuf = 0; + + if ( !ifsim_tid ) { + ifsim_tid = rtems_bsdnet_newproc("IFSM", 10000, ifsim_task, 0); + } + + ihdl.name = ifcfg->irno; + ihdl.hdl = ifsim_isr; + ihdl.handle = sc; + ihdl.on = ifsim_irq_on; + ihdl.off = ifsim_irq_off; + ihdl.isOn = ifsim_irq_ison; + + if ( ! BSP_install_rtems_irq_handler(&ihdl) ) { + printk(DRVNAME"_attach(): installing ISR failed!\n"); + return -1; + } + + if ( ifcfg->hardware_address ) { + memcpy(sc->arpcom.ac_enaddr, ifcfg->hardware_address, ETHER_ADDR_LEN); + } else { + v = ifsim_in(sc, IFSIM_MACA0_REG); + memcpy(sc->arpcom.ac_enaddr, &v, 4); + v = ifsim_in(sc, IFSIM_MACA1_REG); + memcpy(sc->arpcom.ac_enaddr+4, &v, 2); + } + + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = unitName; + + ifp->if_mtu = ifcfg->mtu ? ifcfg->mtu : ETHERMTU; + + ifp->if_init = ifsim_init; + ifp->if_ioctl = ifsim_ioctl; + ifp->if_start = ifsim_start; + ifp->if_output = ether_output; + + ifp->if_watchdog = 0; + ifp->if_timer = 0; + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + if_attach(ifp); + ether_ifattach(ifp); + + return 0; +} |