Browse Source

sys: net: add ng_netdev_eth netdev driver for dev_eth based ethernet devices

dev/timer
Kaspar Schleiser 8 years ago
parent
commit
1c91baed8b
  1. 5
      cpu/native/ng_net/dev_eth_tap.c
  2. 3
      sys/Makefile
  3. 81
      sys/include/net/ng_netdev_eth.h
  4. 1
      sys/net/link_layer/ng_netdev_eth/Makefile
  5. 542
      sys/net/link_layer/ng_netdev_eth/ng_netdev_eth.c
  6. 3
      tests/dev_eth/main.c

5
cpu/native/ng_net/dev_eth_tap.c

@ -118,6 +118,9 @@ static int _recv(dev_eth_t *dev_eth, char *buf, int len) {
"That's not me => Dropped\n",
hdr->dst[0], hdr->dst[1], hdr->dst[2],
hdr->dst[3], hdr->dst[4], hdr->dst[5]);
#ifdef __MACH__
kill(_sigio_child_pid, SIGCONT);
#endif
return 0;
}
/* work around lost signals */
@ -139,7 +142,7 @@ static int _recv(dev_eth_t *dev_eth, char *buf, int len) {
}
else {
#ifdef __MACH__
kill(_sigio_child_pid, SIGCONT);
kill(_sigio_child_pid, SIGCONT);
#endif
}

3
sys/Makefile

@ -161,6 +161,9 @@ endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
DIRS += cpp11-compat
endif
ifneq (,$(filter ng_netdev_eth,$(USEMODULE)))
DIRS += net/link_layer/ng_netdev_eth
endif
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))

81
sys/include/net/ng_netdev_eth.h

@ -0,0 +1,81 @@
/*
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
* Kaspar Schleiser <kaspar@schleiser.de>
* Ell-i open source co-operative
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for
* more details.
*/
/**
* @defgroup ng_netdev_eth dev_eth ethernet interface as netdev device.
* @ingroup net
*
* @brief Allows for usage of dev_eth ethernet interfaces as ethernet netdev
* devices.
*
* @{
*
* @file
* @brief Definitions for @ref ng_netdev_eth
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef NG_NETDEV_ETH_H_
#define NG_NETDEV_ETH_H_
#include <inttypes.h>
#include "kernel_types.h"
#include "net/ng_netdev.h"
#include "net/ng_ethernet/hdr.h"
#include "net/dev_eth.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Definition of the netdev_eth device
* @extends ng_netdev_t
* @internal
*/
typedef struct {
ng_netdev_driver_t *driver; /**< pointer to the devices interface */
ng_netdev_event_cb_t event_cb; /**< netdev event callback */
kernel_pid_t mac_pid; /**< the driver's thread's PID */
dev_eth_t *ethdev; /**< ptr to low-level device handle */
} ng_netdev_eth_t;
/**
* @brief Reference to the netdev_eth driver interface
*/
extern const ng_netdev_driver_t ng_netdev_eth_driver;
/**
* @brief Reference to the netdev_eth device
*/
extern ng_netdev_eth_t ng_netdev_eth; /* XXX: this is only here since I do not know how
* to get the device in the interrupt handler */
/**
* @brief Initialize a given netdev_eth device
*
* @param[out] netdev netdev_eth device to initialize
* @param[in] ethdev handle dev_eth interface to be used
*
* Will initialize ethdev.
*
* @return 0 on success
* @return -ENODEV on invalid device descriptor
*/
int ng_netdev_eth_init(ng_netdev_eth_t *netdev, dev_eth_t *ethdev);
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* NG_NETDEV_ETH_H_ */

1
sys/net/link_layer/ng_netdev_eth/Makefile

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

542
sys/net/link_layer/ng_netdev_eth/ng_netdev_eth.c

@ -0,0 +1,542 @@
/*
* Copyright (C) 2015 Ludwig Ortmann <ludwig.ortmann@fu-berlin.de>,
* Martine Lenders <mlenders@inf.fu-berlin.de>
* Kaspar Schleiser <kaspar@schleiser.de>
*
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for
* more details.
*/
/**
* @ingroup sys_net_link_layer
* @{
*
* @file
*
* @author Ludwig Ortmann <ludwig.ortmann@fu-berlin.de>
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "byteorder.h"
#include "net/ng_ethernet.h"
#include "net/ng_ethertype.h"
#include "net/ng_netdev.h"
#include "net/ng_netif/hdr.h"
#include "net/ng_pkt.h"
#include "net/ng_pktbuf.h"
#include "net/ng_netdev_eth.h"
#include "net/dev_eth.h"
#include "od.h"
#include "thread.h"
#include "utlist.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
ng_netdev_eth_t ng_netdev_eth;
static uint8_t send_buffer[NG_ETHERNET_MAX_LEN];
static uint8_t recv_buffer[NG_ETHERNET_MAX_LEN];
#define _ISR_EVENT_RX (1U)
/* driver function definitions */
static int _send_data(ng_netdev_t *netdev, ng_pktsnip_t *pkt);
static int _add_event_callback(ng_netdev_t *dev, ng_netdev_event_cb_t cb);
static int _rem_event_callback(ng_netdev_t *dev, ng_netdev_event_cb_t cb);
static int _get(ng_netdev_t *dev, ng_netconf_opt_t opt, void *value,
size_t max_len);
static int _set(ng_netdev_t *dev, ng_netconf_opt_t opt, void *value,
size_t value_len);
static void _isr_event(ng_netdev_t *dev, uint32_t event_type);
/* netdev driver struct */
const ng_netdev_driver_t ng_netdev_eth_driver = {
_send_data,
_add_event_callback,
_rem_event_callback,
_get,
_set,
_isr_event,
};
/* internal function definitions */
static inline bool _is_addr_broadcast(uint8_t *addr)
{
return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) &&
(addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff));
}
static inline bool _is_addr_multicast(uint8_t *addr)
{
/* source: http://ieee802.org/secmail/pdfocSP2xXA6d.pdf */
return (addr[0] & 0x01);
}
/* build Ethernet packet from pkt */
static int _marshall_ethernet(ng_netdev_eth_t *dev, uint8_t *buffer, ng_pktsnip_t *pkt);
/* build ISR handler for ISR events */
void _trigger_isr_event(void);
static int _send_data(ng_netdev_t *netdev, ng_pktsnip_t *pkt)
{
int nsent, to_send;
ng_netdev_eth_t *dev = (ng_netdev_eth_t *)netdev;
DEBUG("ng_netdev_eth: send data ");
if (pkt == NULL) {
return -EFAULT;
}
if ((dev == NULL) || (netdev->driver != &ng_netdev_eth_driver)) {
DEBUG("[wrong device descriptor]\n");
ng_pktbuf_release(pkt);
return -ENODEV;
}
DEBUG("\n");
to_send = _marshall_ethernet(dev, send_buffer, pkt);
ng_pktbuf_release(pkt);
if (to_send < 0) {
errno = -to_send;
DEBUG("marshall\n");
return to_send;
}
DEBUG("ng_netdev_eth: send %d bytes\n", to_send);
#if MODULE_OD && defined(ENABLE_DEBUG)
od_hex_dump(send_buffer, to_send, OD_WIDTH_DEFAULT);
#endif
dev_eth_t *ethdev = dev->ethdev;
if ((nsent = ethdev->driver->send(ethdev, (char*)send_buffer, to_send)) < 0) {
DEBUG("write\n");
return -EIO;
}
return nsent;
}
static int _add_event_callback(ng_netdev_t *dev, ng_netdev_event_cb_t cb)
{
DEBUG("ng_netdev_eth: add event callback");
if ((dev == NULL) || (dev->driver != &ng_netdev_eth_driver)) {
DEBUG(" [wrong device descriptor]\n");
return -ENODEV;
}
if (dev->event_cb != NULL) {
DEBUG(" [no space left]\n");
return -ENOBUFS;
}
DEBUG("\n");
dev->event_cb = cb;
return 0;
}
static int _rem_event_callback(ng_netdev_t *dev, ng_netdev_event_cb_t cb)
{
DEBUG("ng_netdev_eth: remove event callback");
if ((dev == NULL) || (dev->driver != &ng_netdev_eth_driver)) {
DEBUG(" [wrong device descriptor]\n");
return -ENODEV;
}
if (dev->event_cb != cb) {
DEBUG(" [not found]\n");
return -ENOENT;
}
DEBUG("\n");
dev->event_cb = NULL;
return 0;
}
/* individual option getters to be called by _get() */
static inline int _get_addr(ng_netdev_eth_t *netdev, uint8_t *value, size_t max_len)
{
if (max_len < NG_ETHERNET_ADDR_LEN) {
/* value buffer not big enough */
return -EOVERFLOW;
}
dev_eth_t *dev = netdev->ethdev;
dev->driver->get_mac_addr(dev, value);
return NG_ETHERNET_ADDR_LEN;
}
static inline int _get_addr_len(uint16_t *value, size_t max_len)
{
if (max_len != sizeof(uint16_t)) {
/* value buffer not big enough */
return -EOVERFLOW;
}
*value = NG_ETHERNET_ADDR_LEN;
return sizeof(uint16_t);
}
static inline int _get_max_pkt_sz(uint16_t *value, size_t max_len)
{
if (max_len != sizeof(uint16_t)) {
/* value buffer not big enough */
return -EOVERFLOW;
}
*value = NG_ETHERNET_MAX_LEN;
return sizeof(uint16_t);
}
static inline int _get_promiscousmode(ng_netdev_eth_t *netdev, ng_netconf_enable_t *value,
size_t max_len)
{
if (max_len != sizeof(ng_netconf_enable_t)) {
/* value buffer not big enough */
return -EOVERFLOW;
}
dev_eth_t *dev = netdev->ethdev;
*value = (ng_netconf_enable_t)dev->driver->get_promiscous(dev);
return sizeof(ng_netconf_enable_t);
}
static int _get(ng_netdev_t *dev, ng_netconf_opt_t opt, void *value,
size_t max_len)
{
DEBUG("ng_netdev_eth: get ");
if ((dev == NULL) || (dev->driver != &ng_netdev_eth_driver)) {
DEBUG("[wrong device descriptor]\n");
return -ENODEV;
}
switch (opt) {
case NETCONF_OPT_ADDRESS:
DEBUG("address\n");
return _get_addr((ng_netdev_eth_t *)dev, value, max_len);
case NETCONF_OPT_ADDR_LEN:
DEBUG("address length\n");
return _get_addr_len(value, max_len);
case NETCONF_OPT_MAX_PACKET_SIZE:
DEBUG("maximum packet size\n");
return _get_max_pkt_sz(value, max_len);
case NETCONF_OPT_PROMISCUOUSMODE:
DEBUG("promiscous mode\n");
return _get_promiscousmode((ng_netdev_eth_t *)dev, value, max_len);
default:
DEBUG("[not supported: %d]\n", opt);
return -ENOTSUP;
}
}
/* individual option getters to be called by _get() */
static inline int _set_promiscousmode(ng_netdev_eth_t *netdev, ng_netconf_enable_t *value,
size_t value_len)
{
if (value_len != sizeof(ng_netconf_enable_t)) {
/* value buffer not big enough */
return -EOVERFLOW;
}
dev_eth_t *dev = netdev->ethdev;
dev->driver->set_promiscous(dev, (uint8_t)*value);
return sizeof(ng_netconf_enable_t);
}
static int _set(ng_netdev_t *dev, ng_netconf_opt_t opt, void *value,
size_t value_len)
{
DEBUG("ng_netdev_eth: set ");
if ((dev == NULL) || (dev->driver != &ng_netdev_eth_driver)) {
DEBUG("[wrong device descriptor]\n");
return -ENODEV;
}
switch (opt) {
case NETCONF_OPT_PROMISCUOUSMODE:
DEBUG("promiscous mode\n");
return _set_promiscousmode((ng_netdev_eth_t *)dev, value, value_len);
default:
DEBUG("[not supported: %d]\n", opt);
return -ENOTSUP;
}
}
/* individual event handlers called by _isr_event() */
static void _rx_event(ng_netdev_eth_t *dev);
static void _isr_event(ng_netdev_t *dev, uint32_t event_type)
{
DEBUG("ng_netdev_eth: ISR event ");
if ((dev == NULL) || (dev->driver != &ng_netdev_eth_driver)) {
return;
}
ng_netdev_eth_t *netdev = (ng_netdev_eth_t*)dev;
switch (event_type) {
case _ISR_EVENT_RX:
DEBUG("[ISR]\n");
#ifdef NETDEV_ETH_DELAY_SEND
DEBUG("netdev_eth: delaying send...\n");
volatile int i = NETDEV_ETH_DELAY_SEND;
while(i--);
DEBUG("netdev_eth: delay done.\n");
#endif
dev_eth_t *ethdev = netdev->ethdev;
ethdev->driver->isr(ethdev);
break;
default:
DEBUG("[unknown event_type]\n");
break;
}
}
static inline void _addr_set_broadcast(uint8_t *dst)
{
memset(dst, 0xff, NG_ETHERNET_ADDR_LEN);
}
#define _IPV6_DST_OFFSET (36) /* sizeof(ipv6_hdr_t) - 4 */
static inline void _addr_set_multicast(uint8_t *dst, ng_pktsnip_t *payload)
{
switch (payload->type) {
#ifdef MODULE_NG_IPV6
case NG_NETTYPE_IPV6:
dst[0] = 0x33;
dst[1] = 0x33;
memcpy(dst + 2, ((uint8_t *)payload->data) + _IPV6_DST_OFFSET, 4);
/* TODO change to proper types when ng_ipv6_hdr_t got merged */
break;
#endif
default:
_addr_set_broadcast(dst);
break;
}
}
static int _marshall_ethernet(ng_netdev_eth_t *dev, uint8_t *buffer, ng_pktsnip_t *pkt)
{
int data_len = 0;
ng_ethernet_hdr_t *hdr = (ng_ethernet_hdr_t *)buffer;
ng_netif_hdr_t *netif_hdr;
ng_pktsnip_t *payload;
if (pkt == NULL) {
DEBUG("ng_netdev_eth: pkt was NULL");
return -EINVAL;
}
payload = pkt->next;
if (pkt->type != NG_NETTYPE_NETIF) {
DEBUG("ng_netdev_eth: First header was not generic netif header\n");
return -EBADMSG;
}
hdr->type = byteorder_htons(ng_nettype_to_ethertype(pkt->next->type));
netif_hdr = pkt->data;
/* set ethernet header */
if (netif_hdr->src_l2addr_len == NG_ETHERNET_ADDR_LEN) {
memcpy(hdr->dst, ng_netif_hdr_get_src_addr(netif_hdr),
netif_hdr->src_l2addr_len);
}
else {
dev_eth_t *ethdev = dev->ethdev;
ethdev->driver->get_mac_addr(ethdev, hdr->src);
}
if (netif_hdr->flags & NG_NETIF_HDR_FLAGS_BROADCAST) {
_addr_set_broadcast(hdr->dst);
}
else if (netif_hdr->flags & NG_NETIF_HDR_FLAGS_MULTICAST) {
_addr_set_multicast(hdr->dst, payload);
}
else if (netif_hdr->dst_l2addr_len == NG_ETHERNET_ADDR_LEN) {
memcpy(hdr->dst, ng_netif_hdr_get_dst_addr(netif_hdr),
NG_ETHERNET_ADDR_LEN);
}
else {
DEBUG("ng_netdev_eth: destination address had unexpected format\n");
return -EBADMSG;
}
DEBUG("ng_netdev_eth: send to %02x:%02x:%02x:%02x:%02x:%02x\n",
hdr->dst[0], hdr->dst[1], hdr->dst[2],
hdr->dst[3], hdr->dst[4], hdr->dst[5]);
data_len += sizeof(ng_ethernet_hdr_t);
while (payload != NULL) {
if ((data_len + payload->size) > NG_ETHERNET_MAX_LEN) {
DEBUG("ng_netdev_eth: Packet too big for ethernet frame\n");
return -ENOBUFS;
}
memcpy(send_buffer + data_len, payload->data, payload->size);
data_len += payload->size;
payload = payload->next;
}
/* Pad to minimum payload size.
* Linux does this on its own, but it doesn't hurt to do it here.
* As of now only tuntaposx needs this. */
if (data_len < (NG_ETHERNET_MIN_LEN)) {
DEBUG("ng_netdev_eth: padding data! (%d -> ", data_len);
memset(send_buffer + data_len, 0, NG_ETHERNET_MIN_LEN - data_len);
data_len = NG_ETHERNET_MIN_LEN;
DEBUG("%d)\n", data_len);
}
return data_len;
}
void dev_eth_isr(dev_eth_t* dev)
{
(void)dev;
msg_t msg;
DEBUG("ng_netdev_eth: Trigger ISR event\n");
/* TODO: check whether this is an input or an output event
TODO: refactor this into general io-signal multiplexer */
msg.type = NG_NETDEV_MSG_TYPE_EVENT;
msg.content.value = _ISR_EVENT_RX;
if (msg_send(&msg, ng_netdev_eth.mac_pid) <= 0) {
puts("dev_eth_isr: possibly lost interrupt.");
}
}
void dev_eth_rx_handler(dev_eth_t* dev) {
(void)dev;
_rx_event(&ng_netdev_eth);
}
void dev_eth_linkstate_handler(dev_eth_t *dev, int newstate)
{
DEBUG("ng_dev_eth: dev=0x%08x link %s\n", (unsigned)dev, newstate ? "UP" : "DOWN");
(void)dev; (void)newstate;
}
static void _rx_event(ng_netdev_eth_t *netdev)
{
dev_eth_t *dev = netdev->ethdev;
int nread = dev->driver->recv(dev, (char*)recv_buffer, NG_ETHERNET_MAX_LEN);
DEBUG("ng_netdev_eth: read %d bytes\n", nread);
if (nread > 0) {
ng_ethernet_hdr_t *hdr = (ng_ethernet_hdr_t *)recv_buffer;
ng_pktsnip_t *netif_hdr, *pkt;
ng_nettype_t receive_type = NG_NETTYPE_UNDEF;
size_t data_len = (nread - sizeof(ng_ethernet_hdr_t));
/* TODO: implement multicast groups? */
netif_hdr = ng_pktbuf_add(NULL, NULL,
sizeof(ng_netif_hdr_t) + (2 * NG_ETHERNET_ADDR_LEN),
NG_NETTYPE_NETIF);
if (netif_hdr == NULL) {
DEBUG("ng_netdev_eth: no space left in packet buffer\n");
return;
}
ng_netif_hdr_init(netif_hdr->data, NG_ETHERNET_ADDR_LEN, NG_ETHERNET_ADDR_LEN);
ng_netif_hdr_set_src_addr(netif_hdr->data, hdr->src, NG_ETHERNET_ADDR_LEN);
ng_netif_hdr_set_dst_addr(netif_hdr->data, hdr->dst, NG_ETHERNET_ADDR_LEN);
((ng_netif_hdr_t *)netif_hdr->data)->if_pid = thread_getpid();
receive_type = ng_nettype_from_ethertype(byteorder_ntohs(hdr->type));
DEBUG("ng_netdev_eth: received packet from %02x:%02x:%02x:%02x:%02x:%02x "
"of length %zu\n",
hdr->src[0], hdr->src[1], hdr->src[2], hdr->src[3], hdr->src[4],
hdr->src[5], data_len);
#if defined(MODULE_OD) && ENABLE_DEBUG
od_hex_dump(hdr, nread, OD_WIDTH_DEFAULT);
#endif
/* Mark netif header and payload for next layer */
if ((pkt = ng_pktbuf_add(netif_hdr, recv_buffer + sizeof(ng_ethernet_hdr_t),
data_len, receive_type)) == NULL) {
ng_pktbuf_release(netif_hdr);
DEBUG("ng_netdev_eth: no space left in packet buffer\n");
return;
}
if (netdev->event_cb != NULL) {
netdev->event_cb(NETDEV_EVENT_RX_COMPLETE, pkt);
}
else {
ng_pktbuf_release(pkt); /* netif_hdr is released automatically too */
}
}
else {
DEBUG("ng_netdev_eth: spurious _rx_event: %d\n", nread);
}
}
int ng_netdev_eth_init(ng_netdev_eth_t *netdev, dev_eth_t *ethdev) {
if ((netdev == NULL) || (ethdev == NULL) || (netdev != &ng_netdev_eth)) {
return -ENODEV;
}
/* initialize low-level driver */
dev_eth_init(ethdev);
/* initialize device descriptor */
netdev->driver = (ng_netdev_driver_t *)(&ng_netdev_eth_driver);
netdev->ethdev = ethdev;
DEBUG("ng_netdev_eth: initialized.\n");
return 0;
}
/**
* @}
*/

3
tests/dev_eth/main.c

@ -34,7 +34,7 @@
#include "net/dev_eth.h"
#include "dev_eth_autoinit.h"
#define ENABLE_DEBUG 1
#define ENABLE_DEBUG 0
#include "debug.h"
kernel_pid_t handler_pid = KERNEL_PID_UNDEF;
@ -75,6 +75,7 @@ void dev_eth_rx_handler(dev_eth_t *dev) {
void dev_eth_linkstate_handler(dev_eth_t *dev, int newstate)
{
DEBUG("dev_eth: dev=0x%08x link %s\n", (unsigned)dev, newstate ? "UP" : "DOWN");
(void)dev; (void)newstate;
}
int main(void)

Loading…
Cancel
Save