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/lm32/shared/net/network.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/lm32/shared/net/network.c')
-rw-r--r-- | bsps/lm32/shared/net/network.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/bsps/lm32/shared/net/network.c b/bsps/lm32/shared/net/network.c new file mode 100644 index 0000000000..2bc518ef25 --- /dev/null +++ b/bsps/lm32/shared/net/network.c @@ -0,0 +1,319 @@ +/* + * RTEMS driver for Minimac2 ethernet IP-core of Milkymist SoC + * + * 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. + * + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis, France + * Copyright (C) 2011 Sebastien Bourdeauducq + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <stdio.h> +#include <string.h> +#include <rtems/bspIo.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/status-checks.h> +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <net/ethernet.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <rtems.h> +#include "bspopts.h" +#include "../include/system_conf.h" +#include "network.h" + +#define CTS_EVENT RTEMS_EVENT_1 +#define RX_EVENT RTEMS_EVENT_1 +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +static struct arpcom arpcom; +static rtems_id rx_daemon_id; +static rtems_id tx_daemon_id; + +static void minimac_init(void *arg); +static int minimac_ioctl(struct ifnet *ifp, ioctl_command_t command, + caddr_t data); +static void minimac_start(struct ifnet *ifp); + +static void rx_daemon(void *arg); +static void tx_daemon(void *arg); +static rtems_isr rx_interrupt_handler(rtems_vector_number vector); +static rtems_isr tx_interrupt_handler(rtems_vector_number vector); + +static bool validate_mac(const char *m) +{ + int i; + + for(i=0;i<6;i++) + if((m[i] != 0x00) && (m[i] != 0xff)) + return true; + return false; +} + +static const char *get_mac_address(void) +{ + const char *flash_mac = (const char *)FLASH_OFFSET_MAC_ADDRESS; + static const char fallback_mac[6] = { 0x10, 0xe2, 0xd5, 0x00, 0x00, 0x00 }; + + if(validate_mac(flash_mac)) + return flash_mac; + else { + printk("Warning: using fallback MAC address\n"); + return fallback_mac; + } +} + +int rtems_minimac_driver_attach(struct rtems_bsdnet_ifconfig *config, + int attaching) +{ + struct ifnet *ifp; + rtems_isr_entry dummy; + int i; + static int registered; + uint8_t *tx_buffer = (uint8_t *)MINIMAC_TX_BASE; + + if(!attaching) { + printk("Minimac driver cannot be detached.\n"); + return 0; + } + + ifp = &(arpcom.ac_if); + + if(registered) { + printk("Minimac driver already in use.\n"); + return 0; + } + registered = 1; + + memcpy(arpcom.ac_enaddr, get_mac_address(), 6); + ifp->if_mtu = ETHERMTU; + ifp->if_unit = 0; + ifp->if_name = "minimac"; + ifp->if_init = minimac_init; + ifp->if_ioctl = minimac_ioctl; + ifp->if_start = minimac_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + if_attach(ifp); + ether_ifattach(ifp); + + rx_daemon_id = rtems_bsdnet_newproc("mrxd", 4096, rx_daemon, NULL); + tx_daemon_id = rtems_bsdnet_newproc("mtxd", 4096, tx_daemon, NULL); + rtems_interrupt_catch(rx_interrupt_handler, MM_IRQ_ETHRX, &dummy); + rtems_interrupt_catch(tx_interrupt_handler, MM_IRQ_ETHTX, &dummy); + + MM_WRITE(MM_MINIMAC_STATE0, MINIMAC_STATE_LOADED); + MM_WRITE(MM_MINIMAC_STATE1, MINIMAC_STATE_LOADED); + + for(i=0;i<7; i++) + tx_buffer[i] = 0x55; + tx_buffer[7] = 0xd5; + MM_WRITE(MM_MINIMAC_SETUP, 0); + rtems_bsdnet_event_send(tx_daemon_id, CTS_EVENT); + + bsp_interrupt_vector_enable(MM_IRQ_ETHRX); + bsp_interrupt_vector_enable(MM_IRQ_ETHTX); + + return 1; +} + +static void minimac_start(struct ifnet *ifp) +{ + rtems_bsdnet_event_send(tx_daemon_id, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +static void minimac_init(void *arg) +{ + struct ifnet *ifp = &arpcom.ac_if; + ifp->if_flags |= IFF_RUNNING; +} + +static void minimac_stop(void) +{ + struct ifnet *ifp = &arpcom.ac_if; + ifp->if_flags &= ~IFF_RUNNING; +} + +static int minimac_ioctl(struct ifnet *ifp, ioctl_command_t command, + caddr_t data) +{ + int error; + + 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: + minimac_stop(); + break; + case IFF_UP: + minimac_init(NULL); + break; + case IFF_UP | IFF_RUNNING: + minimac_stop(); + minimac_init(NULL); + break; + default: + break; + } + break; + + default: + error = EINVAL; + break; + } + return error; +} + + +static rtems_isr rx_interrupt_handler(rtems_vector_number vector) +{ + /* Deassert IRQ line. + * The RX daemon will then read all the slots we marked as empty. + */ + if(MM_READ(MM_MINIMAC_STATE0) == MINIMAC_STATE_PENDING) + MM_WRITE(MM_MINIMAC_STATE0, MINIMAC_STATE_EMPTY); + if(MM_READ(MM_MINIMAC_STATE1) == MINIMAC_STATE_PENDING) + MM_WRITE(MM_MINIMAC_STATE1, MINIMAC_STATE_EMPTY); + + rtems_bsdnet_event_send(rx_daemon_id, RX_EVENT); + + lm32_interrupt_ack(1 << MM_IRQ_ETHRX); +} + +static void receive_packet(uint8_t *buffer, int length) +{ + struct ifnet *ifp = &arpcom.ac_if; + struct mbuf *m; + struct ether_header *eh; + uint32_t computed_crc, net_crc; + + if(length < 64) { + printk("Warning: Ethernet packet too short\n"); + return; + } + + length -= 4; /* strip CRC */ + net_crc = ((uint32_t)buffer[length]) + | ((uint32_t)buffer[length+1] << 8) + | ((uint32_t)buffer[length+2] << 16) + | ((uint32_t)buffer[length+3] << 24); + length -= 8; /* strip preamble */ + computed_crc = ether_crc32_le(&buffer[8], length) ^ 0xffffffff; + if(computed_crc == net_crc) { + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + length -= sizeof(struct ether_header); /* strip Ethernet header */ + memcpy(m->m_data, &buffer[8+sizeof(struct ether_header)], length); + m->m_len = m->m_pkthdr.len = length; + m->m_pkthdr.rcvif = ifp; + eh = (struct ether_header *)&buffer[8]; + ether_input(ifp, eh, m); + } else + printk("Ethernet CRC error: got %08x expected %08x (len=%d)\n", + net_crc, computed_crc, length); +} + +static void rx_daemon(void *arg) +{ + rtems_event_set events; + + while(1) { + rtems_bsdnet_event_receive(RX_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, &events); + + if(MM_READ(MM_MINIMAC_STATE0) == MINIMAC_STATE_EMPTY) { + receive_packet((uint8_t *)MINIMAC_RX0_BASE, MM_READ(MM_MINIMAC_COUNT0)); + MM_WRITE(MM_MINIMAC_STATE0, MINIMAC_STATE_LOADED); + } + if(MM_READ(MM_MINIMAC_STATE1) == MINIMAC_STATE_EMPTY) { + receive_packet((uint8_t *)MINIMAC_RX1_BASE, MM_READ(MM_MINIMAC_COUNT1)); + MM_WRITE(MM_MINIMAC_STATE1, MINIMAC_STATE_LOADED); + } + } +} + +/* RTEMS apparently doesn't support m_length() ... */ +static int copy_mbuf_chain(struct mbuf *m, uint8_t *target) +{ + int len; + + len = 0; + while(m != NULL) { + if(m->m_len > 0) { + m_copydata(m, 0, m->m_len, (caddr_t)(target + len)); + len += m->m_len; + } + m = m->m_next; + } + return len; +} + +static void send_packet(struct ifnet *ifp, struct mbuf *m) +{ + unsigned int len; + unsigned int crc; + uint8_t *tx_buffer = (uint8_t *)(MINIMAC_TX_BASE+8); + + len = copy_mbuf_chain(m, tx_buffer); + for(;len<60;len++) + tx_buffer[len] = 0x00; // Padding + + crc = ether_crc32_le(tx_buffer, len) ^ 0xffffffff; + + tx_buffer[len] = crc & 0xff; + tx_buffer[len+1] = (crc & 0xff00) >> 8; + tx_buffer[len+2] = (crc & 0xff0000) >> 16; + tx_buffer[len+3] = crc >> 24; + + len += 4; // We add 4 bytes of CRC32 + + MM_WRITE(MM_MINIMAC_TXCOUNT, len + 8); +} + +static rtems_isr tx_interrupt_handler(rtems_vector_number vector) +{ + lm32_interrupt_ack(1 << MM_IRQ_ETHTX); + rtems_bsdnet_event_send(tx_daemon_id, CTS_EVENT); +} + +static void tx_daemon(void *arg) +{ + struct ifnet *ifp = &arpcom.ac_if; + rtems_event_set events; + struct mbuf *m; + + while(1) { + rtems_bsdnet_event_receive(START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); + while(1) { + IF_DEQUEUE(&ifp->if_snd, m); + if(m == NULL) + break; + rtems_bsdnet_event_receive(CTS_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, &events); + send_packet(ifp, m); + m_freem(m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} |