if_lpe.c: Port to LibBSD

This commit is contained in:
Sebastian Huber 2022-06-09 10:50:26 +02:00
parent 5b031886e1
commit 192e8d37a3
2 changed files with 234 additions and 248 deletions

View File

@ -221,6 +221,7 @@ class rtems(builder.Module):
'rtems/rtems-legacy-rtrequest.c', 'rtems/rtems-legacy-rtrequest.c',
'rtems/rtems-legacy-newproc.c', 'rtems/rtems-legacy-newproc.c',
'rtems/rtems-legacy-mii.c', 'rtems/rtems-legacy-mii.c',
'sys/arm/lpc/if_lpe.c',
'sys/arm/lpc/lpc_pwr.c', 'sys/arm/lpc/lpc_pwr.c',
'sys/dev/atsam/if_atsam.c', 'sys/dev/atsam/if_atsam.c',
'sys/dev/atsam/if_atsam_media.c', 'sys/dev/atsam/if_atsam_media.c',

View File

@ -22,28 +22,32 @@
#include <machine/rtems-bsd-kernel-space.h> #include <machine/rtems-bsd-kernel-space.h>
#include <errno.h> #include <bsp.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <rtems.h> #if defined(LIBBSP_ARM_LPC24XX_BSP_H) || defined(LIBBSP_ARM_LPC32XX_BSP_H)
#include <rtems/rtems_bsdnet.h>
#include <rtems/rtems_mii_ioctl.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/sockio.h> #include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/bus.h>
#include <machine/bus.h>
#include <net/if.h> #include <net/if.h>
#include <net/ethernet.h>
#include <net/if_arp.h> #include <net/if_arp.h>
#include <netinet/in.h> #include <net/if_dl.h>
#include <netinet/if_ether.h> #include <net/if_media.h>
#include <netinet/in_systm.h> #include <net/if_types.h>
#include <netinet/ip.h> #include <net/if_var.h>
#include <dev/mii/mii.h>
#include <rtems/bsd/bsd.h>
#include <bsp.h> #include <bsp.h>
#include <bsp/irq.h> #include <bsp/irq.h>
@ -128,6 +132,10 @@ typedef struct {
uint32_t powerdown; uint32_t powerdown;
} lpc_eth_controller; } lpc_eth_controller;
#define LPE_LOCK(e) mtx_lock(&(e)->mtx)
#define LPE_UNLOCK(e) mtx_unlock(&(e)->mtx)
static volatile lpc_eth_controller *const lpc_eth = static volatile lpc_eth_controller *const lpc_eth =
(volatile lpc_eth_controller *) LPC_ETH_CONFIG_REG_BASE; (volatile lpc_eth_controller *) LPC_ETH_CONFIG_REG_BASE;
@ -300,10 +308,12 @@ typedef enum {
} lpc_eth_state; } lpc_eth_state;
typedef struct { typedef struct {
struct arpcom arpcom; device_t dev;
struct ifnet *ifp;
struct mtx mtx;
lpc_eth_state state; lpc_eth_state state;
struct rtems_mdio_info mdio;
uint32_t anlpar; uint32_t anlpar;
struct callout watchdog_callout;
rtems_id receive_task; rtems_id receive_task;
rtems_id transmit_task; rtems_id transmit_task;
unsigned rx_unit_count; unsigned rx_unit_count;
@ -338,14 +348,18 @@ typedef struct {
int phy; int phy;
rtems_vector_number interrupt_number; rtems_vector_number interrupt_number;
rtems_id control_task; rtems_id control_task;
int if_flags;
struct ifmedia ifmedia;
} lpc_eth_driver_entry; } lpc_eth_driver_entry;
static lpc_eth_driver_entry lpc_eth_driver_data; static void lpc_eth_interface_watchdog(void *arg);
static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e);
static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e) static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e)
{ {
rtems_status_code sc = rtems_event_transient_send(e->control_task); rtems_status_code sc = rtems_event_transient_send(e->control_task);
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
} }
static void lpc_eth_control_request( static void lpc_eth_control_request(
@ -355,17 +369,14 @@ static void lpc_eth_control_request(
) )
{ {
rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_status_code sc = RTEMS_SUCCESSFUL;
uint32_t nest_count = 0;
e->control_task = rtems_task_self(); e->control_task = rtems_task_self();
sc = rtems_bsdnet_event_send(task, event); sc = rtems_event_send(task, event);
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
nest_count = rtems_bsdnet_semaphore_release_recursive();
sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
rtems_bsdnet_semaphore_obtain_recursive(nest_count);
e->control_task = 0; e->control_task = 0;
} }
@ -418,7 +429,7 @@ static void lpc_eth_interrupt_handler(void *arg)
/* Send events to receive task */ /* Send events to receive task */
if (re != 0) { if (re != 0) {
++e->receive_interrupts; ++e->receive_interrupts;
(void) rtems_bsdnet_event_send(e->receive_task, re); (void) rtems_event_send(e->receive_task, re);
} }
/* Check transmit interrupts */ /* Check transmit interrupts */
@ -433,7 +444,7 @@ static void lpc_eth_interrupt_handler(void *arg)
/* Send events to transmit task */ /* Send events to transmit task */
if (te != 0) { if (te != 0) {
++e->transmit_interrupts; ++e->transmit_interrupts;
(void) rtems_bsdnet_event_send(e->transmit_task, te); (void) rtems_event_send(e->transmit_task, te);
} }
LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te); LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te);
@ -486,7 +497,7 @@ static void lpc_eth_disable_transmit_interrupts(void)
static struct mbuf *lpc_eth_new_mbuf(struct ifnet *ifp, bool wait) static struct mbuf *lpc_eth_new_mbuf(struct ifnet *ifp, bool wait)
{ {
struct mbuf *m = NULL; struct mbuf *m = NULL;
int mw = wait ? M_WAIT : M_DONTWAIT; int mw = wait ? M_WAITOK : M_NOWAIT;
MGETHDR(m, mw, MT_DATA); MGETHDR(m, mw, MT_DATA);
if (m != NULL) { if (m != NULL) {
@ -546,12 +557,12 @@ static bool lpc_eth_add_new_mbuf(
} }
} }
static void lpc_eth_receive_task(void *arg) static void lpc_eth_receive_task(rtems_task_argument arg)
{ {
rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_status_code sc = RTEMS_SUCCESSFUL;
rtems_event_set events = 0; rtems_event_set events = 0;
lpc_eth_driver_entry *const e = (lpc_eth_driver_entry *) arg; lpc_eth_driver_entry *const e = (lpc_eth_driver_entry *) arg;
struct ifnet *const ifp = &e->arpcom.ac_if; struct ifnet *const ifp = e->ifp;
volatile lpc_eth_transfer_descriptor *const desc = e->rx_desc_table; volatile lpc_eth_transfer_descriptor *const desc = e->rx_desc_table;
volatile lpc_eth_receive_status *const status = e->rx_status_table; volatile lpc_eth_receive_status *const status = e->rx_status_table;
struct mbuf **const mbufs = e->rx_mbuf_table; struct mbuf **const mbufs = e->rx_mbuf_table;
@ -564,7 +575,7 @@ static void lpc_eth_receive_task(void *arg)
/* Main event loop */ /* Main event loop */
while (true) { while (true) {
/* Wait for events */ /* Wait for events */
sc = rtems_bsdnet_event_receive( sc = rtems_event_receive(
LPC_ETH_EVENT_INITIALIZE LPC_ETH_EVENT_INITIALIZE
| LPC_ETH_EVENT_STOP | LPC_ETH_EVENT_STOP
| LPC_ETH_EVENT_INTERRUPT, | LPC_ETH_EVENT_INTERRUPT,
@ -572,7 +583,7 @@ static void lpc_eth_receive_task(void *arg)
RTEMS_NO_TIMEOUT, RTEMS_NO_TIMEOUT,
&events &events
); );
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events); LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events);
@ -667,22 +678,17 @@ static void lpc_eth_receive_task(void *arg)
struct mbuf *m = mbufs [consume_index]; struct mbuf *m = mbufs [consume_index];
if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) { if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) {
/* Ethernet header */ /* Discard Ethernet CRC */
struct ether_header *eh = mtod(m, struct ether_header *); int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1 - ETHER_CRC_LEN;
/* Discard Ethernet header and CRC */
int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1
- ETHER_HDR_LEN - ETHER_CRC_LEN;
/* Update mbuf */ /* Update mbuf */
m->m_len = sz; m->m_len = sz;
m->m_pkthdr.len = sz; m->m_pkthdr.len = sz;
m->m_data = mtod(m, char *) + ETHER_HDR_LEN;
LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz); LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz);
/* Hand over */ /* Hand over */
ether_input(ifp, eh, m); (*ifp->if_input)(ifp, m);
/* Increment received frames counter */ /* Increment received frames counter */
++e->received_frames; ++e->received_frames;
@ -776,12 +782,12 @@ static struct mbuf *lpc_eth_next_fragment(
return m; return m;
} }
static void lpc_eth_transmit_task(void *arg) static void lpc_eth_transmit_task(rtems_task_argument arg)
{ {
rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_status_code sc = RTEMS_SUCCESSFUL;
rtems_event_set events = 0; rtems_event_set events = 0;
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
struct ifnet *ifp = &e->arpcom.ac_if; struct ifnet *ifp = e->ifp;
volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
volatile uint32_t *const status = e->tx_status_table; volatile uint32_t *const status = e->tx_status_table;
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
@ -812,7 +818,7 @@ static void lpc_eth_transmit_task(void *arg)
/* Main event loop */ /* Main event loop */
while (true) { while (true) {
/* Wait for events */ /* Wait for events */
sc = rtems_bsdnet_event_receive( sc = rtems_event_receive(
LPC_ETH_EVENT_INITIALIZE LPC_ETH_EVENT_INITIALIZE
| LPC_ETH_EVENT_STOP | LPC_ETH_EVENT_STOP
| LPC_ETH_EVENT_TXSTART | LPC_ETH_EVENT_TXSTART
@ -821,7 +827,7 @@ static void lpc_eth_transmit_task(void *arg)
RTEMS_NO_TIMEOUT, RTEMS_NO_TIMEOUT,
&events &events
); );
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
LPC_ETH_PRINTF("tx: wake up: 0x%08" PRIx32 "\n", events); LPC_ETH_PRINTF("tx: wake up: 0x%08" PRIx32 "\n", events);
@ -1030,7 +1036,7 @@ static void lpc_eth_transmit_task(void *arg)
/* Cache flush of data */ /* Cache flush of data */
rtems_cache_flush_multiple_data_lines( rtems_cache_flush_multiple_data_lines(
(const void *) desc [produce_index].start, (const void *) desc [produce_index].start,
new_frame_length new_frame_length
); );
/* Cache flush of descriptor */ /* Cache flush of descriptor */
@ -1071,7 +1077,7 @@ static void lpc_eth_transmit_task(void *arg)
/* No more fragments? */ /* No more fragments? */
if (m == NULL) { if (m == NULL) {
/* Interface is now inactive */ /* Interface is now inactive */
ifp->if_flags &= ~IFF_OACTIVE; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
} else { } else {
LPC_ETH_PRINTF("tx: enable interrupts\n"); LPC_ETH_PRINTF("tx: enable interrupts\n");
@ -1358,10 +1364,9 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
{ {
int eno = 0; int eno = 0;
rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_status_code sc = RTEMS_SUCCESSFUL;
struct ifnet *ifp = &e->arpcom.ac_if; struct ifnet *ifp = e->ifp;
if (up && e->state == LPC_ETH_STATE_DOWN) { if (up && e->state == LPC_ETH_STATE_DOWN) {
lpc_eth_config_module_enable(); lpc_eth_config_module_enable();
/* Enable RX/TX reset and disable soft reset */ /* Enable RX/TX reset and disable soft reset */
@ -1376,6 +1381,8 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
eno = lpc_eth_phy_up(e); eno = lpc_eth_phy_up(e);
if (eno == 0) { if (eno == 0) {
const uint8_t *eaddr;
/* /*
* We must have a valid external clock from the PHY at this point, * We must have a valid external clock from the PHY at this point,
* otherwise the system bus hangs and only a watchdog reset helps. * otherwise the system bus hangs and only a watchdog reset helps.
@ -1400,12 +1407,12 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
lpc_eth->powerdown = 0; lpc_eth->powerdown = 0;
/* MAC address */ /* MAC address */
lpc_eth->sa0 = ((uint32_t) e->arpcom.ac_enaddr [5] << 8) eaddr = IF_LLADDR(e->ifp);
| (uint32_t) e->arpcom.ac_enaddr [4]; lpc_eth->sa0 = ((uint32_t) eaddr [5] << 8) | (uint32_t) eaddr [4];
lpc_eth->sa1 = ((uint32_t) e->arpcom.ac_enaddr [3] << 8) lpc_eth->sa1 = ((uint32_t) eaddr [3] << 8) | (uint32_t) eaddr [2];
| (uint32_t) e->arpcom.ac_enaddr [2]; lpc_eth->sa2 = ((uint32_t) eaddr [1] << 8) | (uint32_t) eaddr [0];
lpc_eth->sa2 = ((uint32_t) e->arpcom.ac_enaddr [1] << 8)
| (uint32_t) e->arpcom.ac_enaddr [0]; lpc_eth_setup_rxfilter(e);
/* Enable receiver */ /* Enable receiver */
lpc_eth->mac1 = 0x03; lpc_eth->mac1 = 0x03;
@ -1422,26 +1429,25 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
lpc_eth_interrupt_handler, lpc_eth_interrupt_handler,
e e
); );
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
/* Start watchdog timer */ /* Start watchdog timer */
ifp->if_timer = 1; callout_reset(&e->watchdog_callout, hz, lpc_eth_interface_watchdog, e);
/* Change state */ /* Change state */
ifp->if_drv_flags |= IFF_DRV_RUNNING;
e->state = LPC_ETH_STATE_UP; e->state = LPC_ETH_STATE_UP;
} }
if (eno != 0) {
ifp->if_flags &= ~IFF_UP;
}
} else if (!up && e->state == LPC_ETH_STATE_UP) { } else if (!up && e->state == LPC_ETH_STATE_UP) {
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
/* Remove interrupt handler */ /* Remove interrupt handler */
sc = rtems_interrupt_handler_remove( sc = rtems_interrupt_handler_remove(
e->interrupt_number, e->interrupt_number,
lpc_eth_interrupt_handler, lpc_eth_interrupt_handler,
e e
); );
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
/* Stop tasks */ /* Stop tasks */
lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP); lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP);
@ -1452,7 +1458,7 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
lpc_eth_config_module_disable(); lpc_eth_config_module_disable();
/* Stop watchdog timer */ /* Stop watchdog timer */
ifp->if_timer = 0; callout_stop(&e->watchdog_callout);
/* Change state */ /* Change state */
e->state = LPC_ETH_STATE_DOWN; e->state = LPC_ETH_STATE_DOWN;
@ -1463,97 +1469,49 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
static void lpc_eth_interface_init(void *arg) static void lpc_eth_interface_init(void *arg)
{ {
/* Nothing to do */ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
(void) lpc_eth_up_or_down(e, true);
} }
static void lpc_eth_interface_stats(lpc_eth_driver_entry *e) static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e)
{ {
int eno = EIO; struct ifnet *ifp = e->ifp;
int media = 0;
if (e->state == LPC_ETH_STATE_UP) { lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0);
media = IFM_MAKEWORD(0, 0, 0, 0);
eno = rtems_mii_ioctl(&e->mdio, e, SIOCGIFMEDIA, &media);
}
rtems_bsdnet_semaphore_release(); if ((ifp->if_flags & IFF_ALLMULTI)) {
lpc_eth->hashfilterl = 0xffffffff;
if (eno == 0) { lpc_eth->hashfilterh = 0xffffffff;
rtems_ifmedia2str(media, NULL, 0);
printf("\n");
}
printf("received frames: %u\n", e->received_frames);
printf("receive interrupts: %u\n", e->receive_interrupts);
printf("transmitted frames: %u\n", e->transmitted_frames);
printf("transmit interrupts: %u\n", e->transmit_interrupts);
printf("receive drop errors: %u\n", e->receive_drop_errors);
printf("receive overrun errors: %u\n", e->receive_overrun_errors);
printf("receive fragment errors: %u\n", e->receive_fragment_errors);
printf("receive CRC errors: %u\n", e->receive_crc_errors);
printf("receive symbol errors: %u\n", e->receive_symbol_errors);
printf("receive length errors: %u\n", e->receive_length_errors);
printf("receive alignment errors: %u\n", e->receive_alignment_errors);
printf("receive no descriptor errors: %u\n", e->receive_no_descriptor_errors);
printf("receive fatal errors: %u\n", e->receive_fatal_errors);
printf("transmit underrun errors: %u\n", e->transmit_underrun_errors);
printf("transmit late collision errors: %u\n", e->transmit_late_collision_errors);
printf("transmit excessive collision errors: %u\n", e->transmit_excessive_collision_errors);
printf("transmit excessive defer errors: %u\n", e->transmit_excessive_defer_errors);
printf("transmit no descriptor errors: %u\n", e->transmit_no_descriptor_errors);
printf("transmit overflow errors: %u\n", e->transmit_overflow_errors);
printf("transmit fatal errors: %u\n", e->transmit_fatal_errors);
rtems_bsdnet_semaphore_obtain();
}
static int lpc_eth_multicast_control(
bool add,
struct ifreq *ifr,
struct arpcom *ac
)
{
int eno = 0;
if (add) {
eno = ether_addmulti(ifr, ac);
} else { } else {
eno = ether_delmulti(ifr, ac); struct ifmultiaddr *ifma;
}
if (eno == ENETRESET) { lpc_eth->hashfilterl = 0x0;
struct ether_multistep step; lpc_eth->hashfilterh = 0x0;
struct ether_multi *enm;
eno = 0; if_maddr_rlock(ifp);
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
uint32_t crc;
uint32_t index;
lpc_eth->hashfilterl = 0; if (ifma->ifma_addr->sa_family != AF_LINK)
lpc_eth->hashfilterh = 0; continue;
ETHER_FIRST_MULTI(step, ac, enm); /* XXX: ether_crc32_le() does not work, why? */
while (enm != NULL) { crc = ether_crc32_be(
uint64_t addrlo = 0; LLADDR((struct sockaddr_dl *) ifma->ifma_addr),
uint64_t addrhi = 0; ETHER_ADDR_LEN
);
index = (crc >> 23) & 0x3f;
memcpy(&addrlo, enm->enm_addrlo, ETHER_ADDR_LEN); if (index < 32) {
memcpy(&addrhi, enm->enm_addrhi, ETHER_ADDR_LEN); lpc_eth->hashfilterl |= 1U << index;
while (addrlo <= addrhi) { } else {
/* XXX: ether_crc32_le() does not work, why? */ lpc_eth->hashfilterh |= 1U << (index - 32);
uint32_t crc = ether_crc32_be((uint8_t *) &addrlo, ETHER_ADDR_LEN);
uint32_t index = (crc >> 23) & 0x3f;
if (index < 32) {
lpc_eth->hashfilterl |= 1U << index;
} else {
lpc_eth->hashfilterh |= 1U << (index - 32);
}
++addrlo;
} }
ETHER_NEXT_MULTI(step, enm);
} }
if_maddr_runlock(ifp);
} }
return eno;
} }
static int lpc_eth_interface_ioctl( static int lpc_eth_interface_ioctl(
@ -1571,27 +1529,40 @@ static int lpc_eth_interface_ioctl(
switch (cmd) { switch (cmd) {
case SIOCGIFMEDIA: case SIOCGIFMEDIA:
case SIOCSIFMEDIA: case SIOCSIFMEDIA:
rtems_mii_ioctl(&e->mdio, e, cmd, &ifr->ifr_media); eno = ifmedia_ioctl(ifp, ifr, &e->ifmedia, cmd);
break; break;
case SIOCGIFADDR: case SIOCGIFADDR:
case SIOCSIFADDR: case SIOCSIFADDR:
ether_ioctl(ifp, cmd, data); ether_ioctl(ifp, cmd, data);
break; break;
case SIOCSIFFLAGS: case SIOCSIFFLAGS:
eno = lpc_eth_up_or_down(e, (ifp->if_flags & IFF_UP) != 0); LPE_LOCK(e);
if (eno == 0 && (ifp->if_flags & IFF_UP) != 0) { if (ifp->if_flags & IFF_UP) {
lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0); if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
if ((ifp->if_flags ^ e->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) {
lpc_eth_setup_rxfilter(e);
}
} else {
eno = lpc_eth_up_or_down(e, true);
}
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
eno = lpc_eth_up_or_down(e, false);
}
} }
e->if_flags = ifp->if_flags;
LPE_UNLOCK(e);
break; break;
case SIOCADDMULTI: case SIOCADDMULTI:
case SIOCDELMULTI: case SIOCDELMULTI:
eno = lpc_eth_multicast_control(cmd == SIOCADDMULTI, ifr, &e->arpcom); if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
break; LPE_LOCK(e);
case SIO_RTEMS_SHOW_STATS: lpc_eth_setup_rxfilter(e);
lpc_eth_interface_stats(e); LPE_UNLOCK(e);
}
break; break;
default: default:
eno = EINVAL; eno = ether_ioctl(ifp, cmd, data);
break; break;
} }
@ -1603,17 +1574,17 @@ static void lpc_eth_interface_start(struct ifnet *ifp)
rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_status_code sc = RTEMS_SUCCESSFUL;
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc; lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
ifp->if_flags |= IFF_OACTIVE; ifp->if_drv_flags |= IFF_DRV_OACTIVE;
if (e->state == LPC_ETH_STATE_UP) { if (e->state == LPC_ETH_STATE_UP) {
sc = rtems_bsdnet_event_send(e->transmit_task, LPC_ETH_EVENT_TXSTART); sc = rtems_event_send(e->transmit_task, LPC_ETH_EVENT_TXSTART);
assert(sc == RTEMS_SUCCESSFUL); BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
} }
} }
static void lpc_eth_interface_watchdog(struct ifnet *ifp) static void lpc_eth_interface_watchdog(void *arg)
{ {
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc; lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
if (e->state == LPC_ETH_STATE_UP) { if (e->state == LPC_ETH_STATE_UP) {
uint32_t anlpar = lpc_eth_mdio_read_anlpar(e->phy); uint32_t anlpar = lpc_eth_mdio_read_anlpar(e->phy);
@ -1648,79 +1619,77 @@ static void lpc_eth_interface_watchdog(struct ifnet *ifp)
} }
} }
ifp->if_timer = WATCHDOG_TIMEOUT; callout_reset(&e->watchdog_callout, WATCHDOG_TIMEOUT * hz, lpc_eth_interface_watchdog, e);
} }
} }
static unsigned lpc_eth_fixup_unit_count(int count, int default_value, int max) static int lpc_eth_media_change(struct ifnet *ifp)
{ {
if (count <= 0) { (void) ifp;
count = default_value; return EINVAL;
} else if (count > max) {
count = max;
}
return LPC_ETH_CONFIG_UNIT_MULTIPLE
+ (((unsigned) count - 1U) & ~(LPC_ETH_CONFIG_UNIT_MULTIPLE - 1U));
} }
static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config) static void lpc_eth_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{ {
lpc_eth_driver_entry *e = &lpc_eth_driver_data; (void) ifp;
struct ifnet *ifp = &e->arpcom.ac_if;
imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
imr->ifm_active = IFM_ETHER;
if ((lpc_eth->supp & ETH_SUPP_SPEED) != 0) {
imr->ifm_active |= IFM_100_TX;
} else {
imr->ifm_active |= IFM_10_T;
}
if ((lpc_eth->mac2 & ETH_MAC2_FULL_DUPLEX) != 0) {
imr->ifm_active |= IFM_FDX;
} else {
imr->ifm_active |= IFM_HDX;
}
}
int lpc_eth_probe(device_t dev)
{
int unit = device_get_unit(dev);
if (unit != 0) {
return ENXIO;
}
return 0;
}
static int lpc_eth_attach(device_t dev)
{
lpc_eth_driver_entry *e = device_get_softc(dev);
struct ifnet *ifp = NULL;
char *unit_name = NULL; char *unit_name = NULL;
int unit_index = rtems_bsdnet_parse_driver_name(config, &unit_name); int unit_index = device_get_unit(dev);
size_t table_area_size = 0; size_t table_area_size = 0;
char *table_area = NULL; char *table_area = NULL;
char *table_location = NULL; char *table_location = NULL;
rtems_status_code status;
uint8_t eaddr[ETHER_ADDR_LEN];
/* Check parameter */ BSD_ASSERT(e->state == LPC_ETH_STATE_NOT_INITIALIZED);
if (unit_index < 0) {
return 0;
}
if (unit_index != 0) {
goto cleanup;
}
if (config->hardware_address == NULL) {
goto cleanup;
}
if (e->state != LPC_ETH_STATE_NOT_INITIALIZED) {
goto cleanup;
}
/* MDIO */ mtx_init(&e->mtx, device_get_nameunit(e->dev), MTX_NETWORK_LOCK, MTX_DEF);
e->mdio.mdio_r = lpc_eth_mdio_read;
e->mdio.mdio_w = lpc_eth_mdio_write;
e->mdio.has_gmii = 0;
e->anlpar = 0;
/* Interrupt number */ ifmedia_init(&e->ifmedia, 0, lpc_eth_media_change, lpc_eth_media_status);
config->irno = LPC_ETH_CONFIG_INTERRUPT; ifmedia_add(&e->ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&e->ifmedia, IFM_ETHER | IFM_AUTO);
/* Device control */ callout_init_mtx(&e->watchdog_callout, &e->mtx, 0);
config->drv_ctrl = e;
/* Receive unit count */ /* Receive unit count */
e->rx_unit_count = lpc_eth_fixup_unit_count( e->rx_unit_count = LPC_ETH_CONFIG_RX_UNIT_COUNT_DEFAULT;
config->rbuf_count,
LPC_ETH_CONFIG_RX_UNIT_COUNT_DEFAULT,
LPC_ETH_CONFIG_RX_UNIT_COUNT_MAX
);
config->rbuf_count = (int) e->rx_unit_count;
/* Transmit unit count */ /* Transmit unit count */
e->tx_unit_count = lpc_eth_fixup_unit_count( e->tx_unit_count = LPC_ETH_CONFIG_TX_UNIT_COUNT_DEFAULT;
config->xbuf_count,
LPC_ETH_CONFIG_TX_UNIT_COUNT_DEFAULT,
LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX
);
config->xbuf_count = (int) e->tx_unit_count;
/* Remember interrupt number */ /* Remember interrupt number */
e->interrupt_number = config->irno; e->interrupt_number = LPC_ETH_CONFIG_INTERRUPT;
/* Copy MAC address */
memcpy(e->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
/* Allocate and clear table area */ /* Allocate and clear table area */
table_area_size = table_area_size =
@ -1734,7 +1703,7 @@ static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
+ LPC_ETH_CONFIG_TX_BUF_SIZE); + LPC_ETH_CONFIG_TX_BUF_SIZE);
table_area = lpc_eth_config_alloc_table_area(table_area_size); table_area = lpc_eth_config_alloc_table_area(table_area_size);
if (table_area == NULL) { if (table_area == NULL) {
goto cleanup; return ENOMEM;
} }
memset(table_area, 0, table_area_size); memset(table_area, 0, table_area_size);
@ -1762,56 +1731,66 @@ static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
e->tx_buf_table = table_location; e->tx_buf_table = table_location;
/* Set interface data */ /* Set interface data */
e->dev = dev;
e->ifp = ifp = if_alloc(IFT_ETHER);
ifp->if_softc = e; ifp->if_softc = e;
ifp->if_unit = (short) unit_index; if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_name = unit_name;
ifp->if_mtu = (config->mtu > 0) ? (u_long) config->mtu : ETHERMTU;
ifp->if_init = lpc_eth_interface_init; ifp->if_init = lpc_eth_interface_init;
ifp->if_ioctl = lpc_eth_interface_ioctl; ifp->if_ioctl = lpc_eth_interface_ioctl;
ifp->if_start = lpc_eth_interface_start; ifp->if_start = lpc_eth_interface_start;
ifp->if_output = ether_output; ifp->if_qflush = if_qflush;
ifp->if_watchdog = lpc_eth_interface_watchdog; ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
ifp->if_flags = IFF_MULTICAST | IFF_BROADCAST | IFF_SIMPLEX; IFQ_SET_MAXLEN(&ifp->if_snd, LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1);
ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_snd.ifq_drv_maxlen = LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1;
ifp->if_timer = 0; IFQ_SET_READY(&ifp->if_snd);
ifp->if_hdrlen = sizeof(struct ether_header);
rtems_bsd_get_mac_address(device_get_name(e->dev), unit_index, eaddr);
/* Create tasks */ /* Create tasks */
e->receive_task = rtems_bsdnet_newproc( status = rtems_task_create(
"ntrx", rtems_build_name('n', 't', 'r', 'x'),
rtems_bsd_get_task_priority(device_get_name(e->dev)),
4096, 4096,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&e->receive_task
);
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
status = rtems_task_start(
e->receive_task,
lpc_eth_receive_task, lpc_eth_receive_task,
e (rtems_task_argument)e
); );
e->transmit_task = rtems_bsdnet_newproc( BSD_ASSERT(status == RTEMS_SUCCESSFUL);
"nttx", status = rtems_task_create(
rtems_build_name('n', 't', 't', 'x'),
rtems_bsd_get_task_priority(device_get_name(e->dev)),
4096, 4096,
lpc_eth_transmit_task, RTEMS_DEFAULT_MODES,
e RTEMS_DEFAULT_ATTRIBUTES,
&e->transmit_task
); );
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
status = rtems_task_start(
e->transmit_task,
lpc_eth_transmit_task,
(rtems_task_argument)e
);
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
if_link_state_change(e->ifp, LINK_STATE_UP);
/* Change status */ /* Change status */
ifp->if_flags |= IFF_RUNNING;
e->state = LPC_ETH_STATE_DOWN; e->state = LPC_ETH_STATE_DOWN;
/* Attach the interface */ /* Attach the interface */
if_attach(ifp); ether_ifattach(ifp, eaddr);
ether_ifattach(ifp);
return 1;
cleanup:
lpc_eth_config_free_table_area(table_area);
/* FIXME: Type */
free(unit_name, (int) 0xdeadbeef);
return 0; return 0;
} }
static int lpc_eth_detach( static int lpc_eth_detach(device_t dev)
struct rtems_bsdnet_ifconfig *config RTEMS_UNUSED
)
{ {
/* FIXME: Detach the interface from the upper layers? */ /* FIXME: Detach the interface from the upper layers? */
@ -1821,19 +1800,25 @@ static int lpc_eth_detach(
/* FIXME: More cleanup */ /* FIXME: More cleanup */
return 0; return ENXIO;
} }
int lpc_eth_attach_detach( static device_method_t lpe_methods[] = {
struct rtems_bsdnet_ifconfig *config, DEVMETHOD(device_probe, lpc_eth_probe),
int attaching DEVMETHOD(device_attach, lpc_eth_attach),
) DEVMETHOD(device_detach, lpc_eth_detach),
{ DEVMETHOD_END
/* FIXME: Return value */ };
if (attaching) { static driver_t lpe_nexus_driver = {
return lpc_eth_attach(config); "lpe",
} else { lpe_methods,
return lpc_eth_detach(config); sizeof(lpc_eth_driver_entry)
} };
}
static devclass_t lpe_devclass;
DRIVER_MODULE(lpe, nexus, lpe_nexus_driver, lpe_devclass, 0, 0);
MODULE_DEPEND(lpe, nexus, 1, 1, 1);
MODULE_DEPEND(lpe, ether, 1, 1, 1);
#endif /* LIBBSP_ARM_LPC24XX_BSP_H || LIBBSP_ARM_LPC32XX_BSP_H */