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/mpc8260ads | |
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/mpc8260ads')
-rw-r--r-- | bsps/powerpc/mpc8260ads/net/README | 55 | ||||
-rw-r--r-- | bsps/powerpc/mpc8260ads/net/if_hdlcsubr.c | 350 | ||||
-rw-r--r-- | bsps/powerpc/mpc8260ads/net/if_hdlcsubr.h | 34 | ||||
-rw-r--r-- | bsps/powerpc/mpc8260ads/net/network.c | 969 |
4 files changed, 1408 insertions, 0 deletions
diff --git a/bsps/powerpc/mpc8260ads/net/README b/bsps/powerpc/mpc8260ads/net/README new file mode 100644 index 0000000000..d5722557c3 --- /dev/null +++ b/bsps/powerpc/mpc8260ads/net/README @@ -0,0 +1,55 @@ +Networking with HDLC +==================== + +Author: Andy Dachs <a.dachs@sstl.co.uk> +Date: 31st August 2001 +Surrey Satellite Technology Limited + + +The network support in this BSP is not Ethernet support. The +"network" referred to here is a point to point HDLC communication link. +The ADS board does have a 10/100 ethernet port and it would be nice to +get support for that added. + +My requirement is for a WAN so I need the ability to send IP frames over +HDLC. Ultimately this will end up as frame relay support but in the meantime +I'm simply wrapping up the IP packet inside an HDLC frame. There is no +addressing mechanism or mac address attached to the start of the frame. + +This is what is physically transmitted: +<FLAG><IP Frame><CRC><FLAG> + + +The physical link consists of four lines, +TX DATA : Data transmitted +TX CLOCK: Clock for transmitted data. Data source provides clock. +RX DATA : Received data +RX CLOCK: Clock for received data. Data sink accepts clock. + +To connect two entities you require a NULL modem arrangement, i.e. TX data +and Tx clock from one end go into RX data and Rx clock on the other end. + +The MPC8260ADS side of the link is implemented using SCC3 in HDLC mode. The +TX clock is generated by BRG4. The RX clock in input to the board on the CLK5 +input. I built a LVTTL to RS422 converter. + +The other end of the link is a Windows NT PC with WANic400 synchronous +communication card. We bought the card from ImageStream +(http://www.imagestream.com/WANic400.html). You also need the NT +drivers and a cable. There are other distributors but I found these +guys helpful - and they also do Linux drivers. + +The NT WANic driver has a number of modes, one of which is ethernet emulation. +This mode is what is needed to transport IP packets in HDLC frames. + +In libnetworking/net you will find a file called if_hdlcsubr.c containing +the hdlc_input and hdlc_output routines required by network.c. This file was +created by taking out the addressing mechanisms from if_ethersubr.c in the +same directory. There are probably neater ways to do this <any +contributions welcome here>. + + + + + + diff --git a/bsps/powerpc/mpc8260ads/net/if_hdlcsubr.c b/bsps/powerpc/mpc8260ads/net/if_hdlcsubr.c new file mode 100644 index 0000000000..350efdbc22 --- /dev/null +++ b/bsps/powerpc/mpc8260ads/net/if_hdlcsubr.c @@ -0,0 +1,350 @@ +/* + * Created from if_ethersubr.c by Andy Dachs <a.dachs@sstl.co.uk> + * Surrey Satellite Technology Limited (SSTL), 2001 + * Modified (hacked) to support IP frames transmitted over HDLC. This + * all needs tidying up in future but it does actually work for point + * to point communications. I have an SDL WANic400 synchronous + * communications card that comes with Windows NT drivers. The drivers + * support a mode that they call "Ethernet Emulation". That simply + * puts the IP frame in an HDLC frame without any ethernet header. i.e. + * <HDLC Flag><IpFrame><CRC><HDLC Flag>. There is no addressing beyond + * the IP header information so is only suitable for point to point links + * with a single protocol. "At some point" I will add a Frame Relay header + * but at the moment I have difficulties getting the WANic card driver's + * Frame Relay driver to work. + * + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <rtems/rtems_bsdnet.h> + +#include <sys/param.h> +/*#include <sys/systm.h> +#include <sys/kernel.h> */ +#define KERNEL +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <errno.h> +#include <sys/syslog.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/netisr.h> +#include <net/route.h> +#include <net/if_llc.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/ethernet.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> + +#include <stdio.h> + +#include "if_hdlcsubr.h" + +/* +u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +*/ +#define senderr(e) { error = (e); goto bad;} + +/* + * HDLC output routine. + * Just transmit the packet (hardware adds flags and CRC) + */ +int +hdlc_output(ifp, m0, dst, rt0) + register struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; + struct rtentry *rt0; +{ + short type; + int s, error = 0; +#if 0 + u_char edst[6]; +#endif + register struct mbuf *m = m0; + register struct rtentry *rt; + struct mbuf *mcopy = (struct mbuf *)0; +/* register struct ether_header *eh; */ + int off, len = m->m_pkthdr.len; + +/* printk( "hdlc output" ); */ +/* struct arpcom *ac = (struct arpcom *)ifp; */ + + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + senderr(ENETDOWN); + rt = rt0; + if (rt) { + if ((rt->rt_flags & RTF_UP) == 0) { + rt0 = rt = rtalloc1(dst, 1, 0UL); + if (rt0) + rt->rt_refcnt--; + else + senderr(EHOSTUNREACH); + } + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_gwroute == 0) + goto lookup; + if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { + rtfree(rt); rt = rt0; + lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, + 0UL); + if ((rt = rt->rt_gwroute) == 0) + senderr(EHOSTUNREACH); + } + } + if (rt->rt_flags & RTF_REJECT) + if (rt->rt_rmx.rmx_expire == 0 || + rtems_bsdnet_seconds_since_boot() < rt->rt_rmx.rmx_expire) + senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); + } + switch (dst->sa_family) { + + case AF_INET: +#if 0 + if (!arpresolve(ac, rt, m, dst, edst, rt0)) + return (0); /* if not yet resolved */ +#endif + + /* If broadcasting on a simplex interface, loopback a copy */ + if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)) + mcopy = m_copy(m, 0, (int)M_COPYALL); + off = m->m_pkthdr.len - m->m_len; + type = htons(ETHERTYPE_IP); + break; +#if 0 + case AF_UNSPEC: + eh = (struct ether_header *)dst->sa_data; + (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); + type = eh->ether_type; + break; +#endif + default: + printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, + dst->sa_family); + senderr(EAFNOSUPPORT); + } + + if (mcopy) + (void) looutput(ifp, mcopy, dst, rt); +#if 0 + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT); +#endif + + if (m == 0) + senderr(ENOBUFS); + +#if 0 + eh = mtod(m, struct ether_header *); + (void)memcpy(&eh->ether_type, &type, + sizeof(eh->ether_type)); + (void)memcpy(eh->ether_dhost, edst, sizeof (edst)); + (void)memcpy(eh->ether_shost, ac->ac_enaddr, + sizeof(eh->ether_shost)); +#endif + + s = splimp(); + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + splx(s); + senderr(ENOBUFS); + } + IF_ENQUEUE(&ifp->if_snd, m); + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + + ifp->if_obytes += len /*+ sizeof (struct ether_header)*/; + if (m->m_flags & M_MCAST) + ifp->if_omcasts++; + return (error); + +bad: + if (m) + m_freem(m); + return (error); +} + +/* + * Process a received Ethernet packet; + * the packet is in the mbuf chain m without + * the ether header, which is provided separately. + */ +void +hdlc_input(ifp, m) + struct ifnet *ifp; + struct mbuf *m; +{ + register struct ifqueue *inq; + int s; + + struct ether_header eh; + + if ((ifp->if_flags & IFF_UP) == 0) { + m_freem(m); + return; + } + ifp->if_ibytes += m->m_pkthdr.len; +/* + if (memcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, + sizeof(etherbroadcastaddr)) == 0) + m->m_flags |= M_BCAST; + else if (eh->ether_dhost[0] & 1) + m->m_flags |= M_MCAST; +*/ + if (m->m_flags & (M_BCAST|M_MCAST)) + ifp->if_imcasts++; + + /* + * RTEMS addition -- allow application to `tap into' + * the incoming packet stream. + */ + if (ifp->if_tap && (*ifp->if_tap)(ifp, &eh, m)) { + m_freem(m); + return; + } + + schednetisr(NETISR_IP); + inq = &ipintrq; + + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else + IF_ENQUEUE(inq, m); + splx(s); +} + +/* + * Perform common duties while attaching to interface list + */ +void +hdlc_ifattach(ifp) + register struct ifnet *ifp; +{ + register struct ifaddr *ifa; + register struct sockaddr_dl *sdl; + + ifp->if_type = IFT_ETHER; + ifp->if_addrlen = 0; + ifp->if_hdrlen = 0; + ifp->if_mtu = 2048; + if (ifp->if_baudrate == 0) + ifp->if_baudrate = 8000000; + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && + sdl->sdl_family == AF_LINK) { + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ifp->if_addrlen; +/* + memcpy(LLADDR(sdl), + (caddr_t)((struct arpcom *)ifp)->ac_enaddr, + ifp->if_addrlen); +*/ + break; + } +} + +/* SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); */ + +int +hdlc_ioctl(struct ifnet *ifp, int command, caddr_t data) +{ + struct ifaddr *ifa = (struct ifaddr *) data; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0; + + switch (command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#if 0 +#ifdef INET + case AF_INET: + ifp->if_init(ifp->if_softc); /* before arpwhohas */ + + arp_ifinit((struct arpcom *)ifp, ifa); + break; +#endif +#endif + default: + ifp->if_init(ifp->if_softc); + break; + } + break; + + case SIOCGIFADDR: + { + struct sockaddr *sa; + + sa = (struct sockaddr *) & ifr->ifr_data; +/* + memcpy((caddr_t) sa->sa_data, + ((struct arpcom *)ifp->if_softc)->ac_enaddr, + ETHER_ADDR_LEN); +*/ + } + break; + + case SIOCSIFMTU: + /* + * Set the interface MTU. + */ + if (ifr->ifr_mtu > ETHERMTU) { + error = EINVAL; + } else { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + } + return (error); +} diff --git a/bsps/powerpc/mpc8260ads/net/if_hdlcsubr.h b/bsps/powerpc/mpc8260ads/net/if_hdlcsubr.h new file mode 100644 index 0000000000..8289c4bf75 --- /dev/null +++ b/bsps/powerpc/mpc8260ads/net/if_hdlcsubr.h @@ -0,0 +1,34 @@ +/* + * RTEMS/TCPIP driver for MPC8260 SCC HDLC networking + * + * Submitted by Andy Dachs <a.dachs@sstl.co.uk> + * (c) Surrey Satellite Technology Limited, 2001 + * + * On the ADS board the ethernet interface is connected to FCC2 + * but in my application I want TCP over HDLC (see README) + * so will use SCC3 as the network interface. I have other plans + * for the FCCs so am unlikely to add true ethernet support to + * this BSP. Contributions welcome! + * + * COPYRIGHT (c) 1989-1997. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may in + * the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ +#ifndef __IF_HDLCSUBR_H +#define __IF_HDLCSUBR_H + +struct ifnet; +struct mbuf; +struct sockaddr; +struct rtentry; + +void hdlc_ifattach (struct ifnet *); +void hdlc_input (struct ifnet *, struct mbuf *); +int hdlc_output (struct ifnet *, + struct mbuf *, struct sockaddr *, struct rtentry *); +int hdlc_ioctl (struct ifnet *, int , caddr_t ); + +#endif diff --git a/bsps/powerpc/mpc8260ads/net/network.c b/bsps/powerpc/mpc8260ads/net/network.c new file mode 100644 index 0000000000..fb87fc0e64 --- /dev/null +++ b/bsps/powerpc/mpc8260ads/net/network.c @@ -0,0 +1,969 @@ +/* + * RTEMS/TCPIP driver for MPC8260 SCC + * + * Modified for MPC8260 by Andy Dachs <a.dachs@sstl.co.uk> + * Surrey Satellite Technology Limited + * + * On the ADS board the ethernet interface is connected to FCC2 + * but in my application I want TCP over HDLC (see README) + * so will use SCC3 as the network interface. I have other plans + * for the FCCs so am unlikely to add true ethernet support to + * this BSP. Contributions welcome! + * + * Modified for MPC860 by Jay Monkman (jmonkman@frasca.com) + * + * This supports ethernet on either SCC1 or the FEC of the MPC860T. + * Right now, we only do 10 Mbps, even with the FEC. The function + * rtems_m860_enet_driver_attach determines which one to use. Currently, + * only one may be used at a time. + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <bsp/irq.h> +#include <mpc8260.h> +#include <mpc8260/cpm.h> +#include <stdio.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/bspIo.h> + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <errno.h> + +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include "if_hdlcsubr.h" + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses four or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 8 +#define TX_BD_PER_BUF 4 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +extern void m8xx_dump_brgs( void ); + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus CRC (1518). + * Round off to nearest multiple of RBUF_ALIGN. + */ +#define MAX_MTU_SIZE 1518 +/*#define MAX_MTU_SIZE 2050*/ +#define RBUF_ALIGN 4 +#define RBUF_SIZE ((MAX_MTU_SIZE + RBUF_ALIGN) & ~RBUF_ALIGN) + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +/* + * Per-device data + */ +struct m8260_hdlc_struct { + struct ifnet ac_if; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + m8260BufferDescriptor_t *rxBdBase; + m8260BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxNotFirst; + unsigned long rxNotLast; + unsigned long rxInterrupts; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxAbort; + unsigned long rxBadCRC; + unsigned long rxOverrun; + unsigned long rxLostCarrier; + unsigned long txInterrupts; + unsigned long txUnderrun; + unsigned long txLostCarrier; + unsigned long txRawWait; +}; +static struct m8260_hdlc_struct hdlc_driver[NIFACES]; + +static void m8xx_scc3_hdlc_on(const rtems_irq_connect_data* ptr) +{ +} + +static void m8xx_scc3_hdlc_off(const rtems_irq_connect_data* ptr) +{ + /* + * Please put relevant code there + */ +} + +static int m8xx_scc3_hdlc_isOn(const rtems_irq_connect_data* ptr) +{ + return BSP_irq_enabled_at_cpm (ptr->name); +} + +/* + * SCC interrupt handler + * TBD: Can we work out which SCC generated the interrupt from the + * value of v? If so we can use the same handler for multiple + * SCCs. + */ +static void +m8xx_scc3_interrupt_handler (rtems_irq_hdl_param unused) +{ + /* + * Frame received? + */ + if ((m8260.scc3.sccm & M8260_SCCE_RXF) && + (m8260.scc3.scce & M8260_SCCE_RXF) ) { + m8260.scc3.scce = M8260_SCCE_RXF; +/* m8260.scc3.sccm &= ~M8260_SCCE_RXF; */ + hdlc_driver[0].rxInterrupts++; + rtems_bsdnet_event_send (hdlc_driver[0].rxDaemonTid, INTERRUPT_EVENT); +/* + printk( "Rx " ); +*/ + } + + /* + * Buffer transmitted or transmitter error? + */ + if ((m8260.scc3.sccm & (M8260_SCCE_TX | M8260_SCCE_TXE) ) && + (m8260.scc3.scce & (M8260_SCCE_TX | M8260_SCCE_TXE) )) { + m8260.scc3.scce = M8260_SCCE_TX | M8260_SCCE_TXE; +/* m8260.scc3.sccm &= ~(M8260_SCCE_TX | M8260_SCCE_TXE); */ + hdlc_driver[0].txInterrupts++; + rtems_bsdnet_event_send (hdlc_driver[0].txDaemonTid, INTERRUPT_EVENT); +/* + printk( "Tx " ); +*/ + } + +#if 0 + m8260.sipnr_l = M8260_SIMASK_SCC3; /* Clear SCC3 interrupt-in-service bit */ +#endif +} + +static rtems_irq_connect_data hdlcSCC3IrqData = { + BSP_CPM_IRQ_SCC3, + (rtems_irq_hdl) m8xx_scc3_interrupt_handler, + NULL, + (rtems_irq_enable) m8xx_scc3_hdlc_on, + (rtems_irq_disable) m8xx_scc3_hdlc_off, + (rtems_irq_is_enabled)m8xx_scc3_hdlc_isOn +}; + +/* + * Initialize the SCC hardware + * Configure I/O ports for SCC3 + * Internal Tx clock, External Rx clock + */ +static void +m8260_scc_initialize_hardware (struct m8260_hdlc_struct *sc) +{ + int i; + int brg; + + rtems_status_code status; + + /* RxD PB14 */ + m8260.pparb |= 0x00020000; + m8260.psorb &= ~0x00020000; + m8260.pdirb &= ~0x00020000; + + /* RxC (CLK5) PC27 */ + m8260.pparc |= 0x00000010; + m8260.psorc &= ~0x00000010; + m8260.pdirc &= ~0x00000010; + + /* TxD PD24 and TxC PD10 (BRG4) */ + m8260.ppard |= 0x00200080; + m8260.psord |= 0x00200000; + m8260.psord &= ~0x00000080; + m8260.pdird |= 0x00200080; + + /* External Rx Clock from CLK5 */ + if( m8xx_get_clk( M8xx_CLK_5 ) == -1 ) + printk( "Error allocating CLK5 for network device.\n" ); + else + m8260.cmxscr |= 0x00002000; + + /* Internal Tx Clock from BRG4 */ + if( (brg = m8xx_get_brg(M8xx_BRG_4, 8000000 )) == -1 ) + printk( "Error allocating BRG for network device\n" ); + else + m8260.cmxscr |= ((unsigned)brg << 8); + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc (sc->rxBdCount * sizeof *sc->rxMbuf, + M_MBUF, M_NOWAIT); + sc->txMbuf = malloc (sc->txBdCount * sizeof *sc->txMbuf, + M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic ("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = m8xx_bd_allocate (sc->rxBdCount); + sc->txBdBase = m8xx_bd_allocate (sc->txBdCount); + + m8260.scc3p.rbase = (char *)sc->rxBdBase - (char *)&m8260; + m8260.scc3p.tbase = (char *)sc->txBdBase - (char *)&m8260; + + /* + * Send "Init parameters" command + */ + + m8xx_cp_execute_cmd (M8260_CR_OP_INIT_RX_TX | M8260_CR_SCC3 ); + + /* + * Set receive and transmit function codes + */ + m8260.scc3p.rfcr = M8260_RFCR_MOT | M8260_RFCR_60X_BUS; + m8260.scc3p.tfcr = M8260_TFCR_MOT | M8260_TFCR_60X_BUS; + + /* + * Set maximum receive buffer length + */ + m8260.scc3p.mrblr = RBUF_SIZE; + + m8260.scc3p.un.hdlc.c_mask = 0xF0B8; + m8260.scc3p.un.hdlc.c_pres = 0xFFFF; + m8260.scc3p.un.hdlc.disfc = 0; + m8260.scc3p.un.hdlc.crcec = 0; + m8260.scc3p.un.hdlc.abtsc = 0; + m8260.scc3p.un.hdlc.nmarc = 0; + m8260.scc3p.un.hdlc.retrc = 0; + m8260.scc3p.un.hdlc.rfthr = 1; + m8260.scc3p.un.hdlc.mflr = RBUF_SIZE; + + m8260.scc3p.un.hdlc.hmask = 0x0000; /* promiscuous */ + + m8260.scc3p.un.hdlc.haddr1 = 0xFFFF; /* Broadcast address */ + m8260.scc3p.un.hdlc.haddr2 = 0xFFFF; /* Station address */ + m8260.scc3p.un.hdlc.haddr3 = 0xFFFF; /* Dummy */ + m8260.scc3p.un.hdlc.haddr4 = 0xFFFF; /* Dummy */ + + /* + * Send "Init parameters" command + */ +/* + m8xx_cp_execute_cmd (M8260_CR_OP_INIT_RX_TX | M8260_CR_SCC3 ); +*/ + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) { + (sc->rxBdBase + i)->status = 0; + } + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + (sc->txBdBase + i)->status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + m8260.scc3.sccm = 0; /* No interrupts unmasked till necessary */ + + /* + * Clear any outstanding events + */ + m8260.scc3.scce = 0xFFFF; + + /* + * Set up interrupts + */ + status = BSP_install_rtems_irq_handler (&hdlcSCC3IrqData); +/* + printk( "status = %d, Success = %d\n", status, RTEMS_SUCCESSFUL ); +*/ + if (status != 1 /*RTEMS_SUCCESSFUL*/ ) { + rtems_panic ("Can't attach M8260 SCC3 interrupt handler: %s\n", + rtems_status_text (status)); + } + m8260.scc3.sccm = 0; /* No interrupts unmasked till necessary */ + + m8260.scc3.gsmr_h = 0; + m8260.scc3.gsmr_l = 0x10000000; + m8260.scc3.dsr = 0x7E7E; /* flag character */ + m8260.scc3.psmr = 0x2000; /* 2 flags between Tx'd frames */ + +/* printk("scc3 init\n" ); */ + + m8260.scc3.gsmr_l |= 0x00000030; /* Set ENR and ENT to enable Rx and Tx */ + +} + +/* + * Soak up buffer descriptors that have been sent + * Note that a buffer descriptor can't be retired as soon as it becomes + * ready. The MC68360 Errata (May 96) says that, "If an Ethernet frame is + * made up of multiple buffers, the user should not reuse the first buffer + * descriptor until the last buffer descriptor of the frame has had its + * ready bit cleared by the CPM". + */ +static void +m8260Enet_retire_tx_bd (struct m8260_hdlc_struct *sc) +{ + uint16_t status; + int i; + int nRetired; + struct mbuf *m, *n; + + i = sc->txBdTail; + nRetired = 0; + while ((sc->txBdActiveCount != 0) + && (((status = (sc->txBdBase + i)->status) & M8260_BD_READY) == 0)) { + /* + * See if anything went wrong + */ + if (status & (M8260_BD_UNDERRUN | + M8260_BD_CTS_LOST)) { + /* + * Check for errors which stop the transmitter. + */ + if( status & M8260_BD_UNDERRUN ) { + hdlc_driver[0].txUnderrun++; + + /* + * Restart the transmitter + */ + /* FIXME: this should get executed only if using the SCC */ + m8xx_cp_execute_cmd (M8260_CR_OP_RESTART_TX | M8260_CR_SCC3); + } + if (status & M8260_BD_CTS_LOST) + hdlc_driver[0].txLostCarrier++; + } + nRetired++; + if (status & M8260_BD_LAST) { + /* + * A full frame has been transmitted. + * Free all the associated buffer descriptors. + */ + sc->txBdActiveCount -= nRetired; + while (nRetired) { + nRetired--; + m = sc->txMbuf[sc->txBdTail]; + MFREE (m, n); + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + } + } + if (++i == sc->txBdCount) + i = 0; + } +} + +/* + * reader task + */ +static void +scc_rxDaemon (void *arg) +{ + struct m8260_hdlc_struct *sc = (struct m8260_hdlc_struct *)arg; + struct ifnet *ifp = &sc->ac_if; + struct mbuf *m; + uint16_t status; + m8260BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + rxBd->status = M8260_BD_EMPTY | M8260_BD_INTERRUPT; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= M8260_BD_WRAP; + break; + } + } + +/* + m8260.scc3.sccm |= M8260_SCCE_RXF; +*/ + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & M8260_BD_EMPTY) { + /* + * Clear old events + */ + + m8260.scc3.scce = M8260_SCCE_RXF; + + /* + * Wait for packet + * Note that the buffer descriptor is checked + * *before* the event wait -- this catches the + * possibility that a packet arrived between the + * `if' above, and the clearing of the event register. + */ + while ((status = rxBd->status) & M8260_BD_EMPTY) { + rtems_event_set events; + + /* + * Unmask RXF (Full frame received) event + */ + m8260.scc3.sccm |= M8260_SCCE_RXF; + +/* printk( "Rxdwait "); */ + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + +/* printk( "Rxd " ); */ + } + } + + /* + * Check that packet is valid + */ + if ((status & (M8260_BD_LAST | + M8260_BD_FIRST_IN_FRAME | + M8260_BD_LONG | + M8260_BD_NONALIGNED | + M8260_BD_ABORT | + M8260_BD_CRC_ERROR | + M8260_BD_OVERRUN /*| + M8260_BD_CARRIER_LOST*/)) == + (M8260_BD_LAST | + M8260_BD_FIRST_IN_FRAME ) ) { + +/* printk( "RxV " ); */ + +/* + * Invalidate the buffer for this descriptor + */ + + rtems_cache_invalidate_multiple_data_lines((void *)rxBd->buffer, rxBd->length); + + m = sc->rxMbuf[rxBdIndex]; + + /* strip off HDLC CRC */ + m->m_len = m->m_pkthdr.len = rxBd->length - sizeof(uint16_t); + + hdlc_input( ifp, m ); + + /* + * Allocate a new mbuf + */ + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + } + else { + printk( "RxErr[%04X,%d]", status, rxBd->length ); + /* + * Something went wrong with the reception + */ + if (!(status & M8260_BD_LAST)) + sc->rxNotLast++; + if (!(status & M8260_BD_FIRST_IN_FRAME)) + sc->rxNotFirst++; + + if (status & M8260_BD_LONG) + sc->rxGiant++; + if (status & M8260_BD_NONALIGNED) + sc->rxNonOctet++; + if (status & M8260_BD_ABORT) + sc->rxAbort++; + if (status & M8260_BD_CRC_ERROR) + sc->rxBadCRC++; + if (status & M8260_BD_OVERRUN) + sc->rxOverrun++; + if (status & M8260_BD_CARRIER_LOST) + sc->rxLostCarrier++; + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & (M8260_BD_WRAP | M8260_BD_INTERRUPT)) | + M8260_BD_EMPTY; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +scc_sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct m8260_hdlc_struct *sc = ifp->if_softc; + volatile m8260BufferDescriptor_t *firstTxBd, *txBd; + struct mbuf *l = NULL; + uint16_t status; + int nAdded; + + /* + * Free up buffer descriptors + */ + m8260Enet_retire_tx_bd (sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + txBd = firstTxBd = sc->txBdBase + sc->txBdHead; + +/* + m8260.scc3.sccm |= (M8260_SCCE_TX | M8260_SCCE_TXE); +*/ + + for (;;) { + /* + * Wait for buffer descriptor to become available. + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events + */ + m8260.scc3.scce = M8260_SCCE_TX | M8260_SCCE_TXE; + + /* + * Wait for buffer descriptor to become available. + * Note that the buffer descriptors are checked + * *before* * entering the wait loop -- this catches + * the possibility that a buffer descriptor became + * available between the `if' above, and the clearing + * of the event register. + * This is to catch the case where the transmitter + * stops in the middle of a frame -- and only the + * last buffer descriptor in a frame can generate + * an interrupt. + */ + m8260Enet_retire_tx_bd (sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + + /* + * Unmask TX (buffer transmitted) event + */ + m8260.scc3.sccm |= (M8260_SCCE_TX | M8260_SCCE_TXE); + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + m8260Enet_retire_tx_bd (sc); + } + } + + /* + * Don't set the READY flag till the + * whole packet has been readied. + */ + status = nAdded ? M8260_BD_READY : 0; + + /* + * FIXME: Why not deal with empty mbufs at at higher level? + * The IP fragmentation routine in ip_output + * can produce packet fragments with zero length. + * I think that ip_output should be changed to get + * rid of these zero-length mbufs, but for now, + * I'll deal with them here. + */ + if (m->m_len) { + /* + * Fill in the buffer descriptor + */ + + txBd->buffer = mtod (m, void *); + txBd->length = m->m_len; + + /* + * Flush the buffer for this descriptor + */ + + rtems_cache_flush_multiple_data_lines((void *)txBd->buffer, txBd->length); + +/* throw off the header for Ethernet Emulation mode */ +/* + txBd->buffer = mtod (m, void *); + txBd->buffer += sizeof( struct ether_header ) + 2; + txBd->length = m->m_len - sizeof( struct ether_header ) - 2; +*/ + sc->txMbuf[sc->txBdHead] = m; + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= M8260_BD_WRAP; + sc->txBdHead = 0; + } + l = m; + m = m->m_next; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE (m, n); + m = n; + if (l != NULL) + l->m_next = m; + } + + /* + * Set the transmit buffer status. + * Break out of the loop if this mbuf is the last in the frame. + */ + if (m == NULL) { + if (nAdded) { + status |= M8260_BD_LAST | M8260_BD_TX_CRC | M8260_BD_INTERRUPT; + txBd->status = status; + firstTxBd->status |= M8260_BD_READY; + sc->txBdActiveCount += nAdded; + } + break; + } + txBd->status = status; + txBd = sc->txBdBase + sc->txBdHead; + } +} + +/* + * Driver transmit daemon + */ +void +scc_txDaemon (void *arg) +{ + struct m8260_hdlc_struct *sc = (struct m8260_hdlc_struct *)arg; + struct ifnet *ifp = &sc->ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive (START_TRANSMIT_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; + + scc_sendpacket (ifp, m); + + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +/* + * Send packet (caller provides header). + */ +static void +m8260_hdlc_start (struct ifnet *ifp) +{ + struct m8260_hdlc_struct *sc = ifp->if_softc; + + rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +/* + * Initialize and start the device + */ +static void +scc_init (void *arg) +{ + struct m8260_hdlc_struct *sc = arg; + struct ifnet *ifp = &sc->ac_if; + + if (sc->txDaemonTid == 0) { + + /* + * Set up SCC hardware + */ + m8260_scc_initialize_hardware (sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, scc_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, scc_rxDaemon, sc); + + } + +#if 0 + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + m8260.scc3.psmr |= 0x200; + else + m8260.scc3.psmr &= ~0x200; +#endif + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + m8260.scc3.gsmr_l |= 0x30; +} + +/* + * Stop the device + */ +static void +scc_stop (struct m8260_hdlc_struct *sc) +{ + struct ifnet *ifp = &sc->ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + m8260.scc3.gsmr_l &= ~0x30; +} + +/* + * Show interface statistics + */ +static void +hdlc_stats (struct m8260_hdlc_struct *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Giant:%-8lu", sc->rxGiant); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Overrun:%-8lu", sc->rxOverrun); + printf (" No Carrier:%-8lu\n", sc->rxLostCarrier); + printf (" Discarded:%-8lu\n", (unsigned long)m8260.scc3p.un.hdlc.disfc); + + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" No Carrier:%-8lu", sc->txLostCarrier); + printf (" Underrun:%-8lu\n", sc->txUnderrun); + printf (" Raw output wait:%-8lu\n", sc->txRawWait); +} + +/* + * Driver ioctl handler + */ +static int +scc_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct m8260_hdlc_struct *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + hdlc_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + scc_stop (sc); + break; + + case IFF_UP: + scc_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + scc_stop (sc); + scc_init (sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + hdlc_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +/* + * Attach an SCC driver to the system + */ +int +rtems_scc3_driver_attach (struct rtems_bsdnet_ifconfig *config) +{ + struct m8260_hdlc_struct *sc; + struct ifnet *ifp; + int mtu; + int i; + + /* + * Find a free driver + */ + for (i = 0 ; i < NIFACES ; i++) { + sc = &hdlc_driver[i]; + ifp = &sc->ac_if; + if (ifp->if_softc == NULL) + break; + } + if (i >= NIFACES) { + printf ("Too many SCC drivers.\n"); + return 0; + } + +#if 0 + /* + * Process options + */ + + if (config->hardware_address) { + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + else { + sc->arpcom.ac_enaddr[0] = 0x44; + sc->arpcom.ac_enaddr[1] = 0x22; + sc->arpcom.ac_enaddr[2] = 0x33; + sc->arpcom.ac_enaddr[3] = 0x33; + sc->arpcom.ac_enaddr[4] = 0x22; + sc->arpcom.ac_enaddr[5] = 0x44; + } +#endif + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + sc->acceptBroadcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = i + 1; + ifp->if_name = "eth"; + ifp->if_mtu = mtu; + ifp->if_init = scc_init; + ifp->if_ioctl = scc_ioctl; + ifp->if_start = m8260_hdlc_start; + ifp->if_output = hdlc_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | /*IFF_PROMISC |*/ IFF_NOARP; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface + */ + if_attach (ifp); + hdlc_ifattach (ifp); + return 1; +}; + +int +rtems_enet_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching) +{ + return rtems_scc3_driver_attach( config ); + +/* + if ((m8260.fec.mii_data & 0xffff) == 0x2000) { + return rtems_fec_driver_attach(config); + } + else { + return rtems_scc1_driver_attach(config); + } +*/ +} |