
9 changed files with 575 additions and 0 deletions
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 net_gnrc |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief netdev2 gnrc glue code interface |
||||
* |
||||
* This interface is supposed to provide common adaption code between the |
||||
* low-level network device interface "netdev2" and the gnrc network stack. |
||||
* |
||||
* GNRC sends around "gnrc_pktsnip_t" structures, but netdev can only handle |
||||
* "struct iovec" structures when sending, or a flat buffer when receiving. |
||||
* |
||||
* The purpose of gnrc_netdev is to bring these two interfaces together. |
||||
* |
||||
* @author Kaspar Schleiser <kaspar@schleiser.de> |
||||
*/ |
||||
|
||||
#ifndef GNRC_NETDEV2_H |
||||
#define GNRC_NETDEV2_H |
||||
|
||||
#include "kernel_types.h" |
||||
#include "net/netdev2.h" |
||||
#include "net/gnrc.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define NETDEV2_MSG_TYPE_EVENT 0x1234 |
||||
|
||||
typedef struct gnrc_netdev2 gnrc_netdev2_t; |
||||
|
||||
/**
|
||||
* @brief Structure holding gnrc netdev2 adapter state |
||||
* |
||||
* This structure is supposed to hold any state parameters needed |
||||
* to use a netdev2 device from gnrc. |
||||
* |
||||
* It can be extended |
||||
*/ |
||||
struct gnrc_netdev2 { |
||||
/**
|
||||
* @brief Send a pktsnip using this device |
||||
* |
||||
* This function should convert the pktsnip into a format |
||||
* the underlying device understands and send it. |
||||
*/ |
||||
int (*send)(gnrc_netdev2_t *dev, gnrc_pktsnip_t *snip); |
||||
|
||||
/**
|
||||
* @brief Receive a pktsnip from this device |
||||
* |
||||
* This function should receive a raw frame from the underlying |
||||
* device and convert it into a pktsnip while adding a netif header |
||||
* and possibly marking out higher-layer headers. |
||||
*/ |
||||
gnrc_pktsnip_t * (*recv)(gnrc_netdev2_t *dev); |
||||
|
||||
/**
|
||||
* @brief netdev2 handle this adapter is working with |
||||
*/ |
||||
netdev2_t *dev; |
||||
|
||||
/**
|
||||
* @brief PID of this adapter for netapi messages |
||||
*/ |
||||
kernel_pid_t pid; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Initialize gnrc netdev2 handler thread |
||||
* |
||||
* @param[in] stack ptr to preallocated stack buffer |
||||
* @param[in] stacksize size of stack buffer |
||||
* @param[in] priority priority of thread |
||||
* @param[in] name name of thread |
||||
* @param[in] gnrc_netdev2 ptr to netdev2 device to handle in created thread |
||||
* |
||||
* @return pid of created thread |
||||
* @return KERNEL_PID_UNDEF on error |
||||
*/ |
||||
kernel_pid_t gnrc_netdev2_init(char *stack, int stacksize, char priority, |
||||
const char *name, gnrc_netdev2_t *gnrc_netdev2); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* GNRC_NETDEV2_H */ |
||||
/** @} */ |
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 net_gnrc |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief netdev2 gnrc ethernet glue code interface |
||||
* |
||||
* @author Kaspar Schleiser <kaspar@schleiser.de> |
||||
*/ |
||||
|
||||
#ifndef GNRC_NETDEV2_ETH_H |
||||
#define GNRC_NETDEV2_ETH_H |
||||
|
||||
#include "net/gnrc/gnrc_netdev2.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/**
|
||||
* @brief Initialize gnrc handler thread for netdev2 ethernet device |
||||
* |
||||
* @param[in] gnrc_netdev2 gnrc_netdev2 struct to initialize |
||||
* @param[in] dev netdev2 device to handle |
||||
* |
||||
* @return 1 on success |
||||
* @return <=0 on error |
||||
*/ |
||||
int gnrc_netdev2_eth_init(gnrc_netdev2_t *gnrc_netdev2, netdev2_t *dev); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* GNRC_NETDEV2_ETH_H */ |
||||
/** @} */ |
@ -0,0 +1,3 @@
|
||||
MODULE = gnrc_netdev2
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin |
||||
* 2015 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 net |
||||
* @file |
||||
* @brief Glue for netdev2 devices to netapi |
||||
* |
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de> |
||||
* @author Kaspar Schleiser <kaspar@schleiser.de> |
||||
* @} |
||||
*/ |
||||
|
||||
#include <errno.h> |
||||
|
||||
#include "kernel.h" |
||||
#include "msg.h" |
||||
#include "thread.h" |
||||
|
||||
#include "net/gnrc.h" |
||||
#include "net/gnrc/nettype.h" |
||||
#include "net/netdev2.h" |
||||
|
||||
#include "net/gnrc/gnrc_netdev2.h" |
||||
#include "net/ethernet/hdr.h" |
||||
|
||||
#define ENABLE_DEBUG (0) |
||||
#include "debug.h" |
||||
|
||||
#if defined(MODULE_OD) && ENABLE_DEBUG |
||||
#include "od.h" |
||||
#endif |
||||
|
||||
#define NETDEV2_NETAPI_MSG_QUEUE_SIZE 8 |
||||
|
||||
static void _pass_on_packet(gnrc_pktsnip_t *pkt); |
||||
|
||||
/**
|
||||
* @brief Function called by the device driver on device events |
||||
* |
||||
* @param[in] event type of event |
||||
* @param[in] data optional parameter |
||||
*/ |
||||
static void _event_cb(netdev2_t *dev, netdev2_event_t event, void *data) |
||||
{ |
||||
(void) data; |
||||
gnrc_netdev2_t *gnrc_netdev2 = (gnrc_netdev2_t*) dev->isr_arg; |
||||
|
||||
if (event == NETDEV2_EVENT_ISR) { |
||||
msg_t msg; |
||||
|
||||
msg.type = NETDEV2_MSG_TYPE_EVENT; |
||||
msg.content.ptr = (void*) gnrc_netdev2; |
||||
|
||||
if (msg_send(&msg, gnrc_netdev2->pid) <= 0) { |
||||
puts("gnrc_netdev2: possibly lost interrupt."); |
||||
} |
||||
} |
||||
else { |
||||
DEBUG("gnrc_netdev2: event triggered -> %i\n", event); |
||||
switch(event) { |
||||
case NETDEV2_EVENT_RX_COMPLETE: |
||||
{ |
||||
gnrc_pktsnip_t *pkt = gnrc_netdev2->recv(gnrc_netdev2); |
||||
|
||||
if (pkt) { |
||||
_pass_on_packet(pkt); |
||||
} |
||||
|
||||
break; |
||||
} |
||||
default: |
||||
DEBUG("gnrc_netdev2: warning: unhandled event %u.\n", event); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void _pass_on_packet(gnrc_pktsnip_t *pkt) |
||||
{ |
||||
gnrc_netreg_entry_t *sendto; |
||||
|
||||
/* find out, who to send the packet to */ |
||||
sendto = gnrc_netreg_lookup(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL); |
||||
|
||||
/* throw away packet if no one is interested */ |
||||
if (sendto == NULL) { |
||||
DEBUG("gnrc_netdev2: unable to forward packet of type %i\n", pkt->type); |
||||
gnrc_pktbuf_release(pkt); |
||||
return; |
||||
} |
||||
|
||||
/* send the packet to everyone interested in it's type */ |
||||
gnrc_pktbuf_hold(pkt, gnrc_netreg_num(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL) - 1); |
||||
while (sendto != NULL) { |
||||
DEBUG("gnrc_netdev2: sending pkt %p to PID %u\n", (void*)pkt, sendto->pid); |
||||
gnrc_netapi_receive(sendto->pid, pkt); |
||||
sendto = gnrc_netreg_getnext(sendto); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief Startup code and event loop of the gnrc_netdev2 layer |
||||
* |
||||
* @param[in] args expects a pointer to the underlying netdev device |
||||
* |
||||
* @return never returns |
||||
*/ |
||||
static void *_gnrc_netdev2_thread(void *args) |
||||
{ |
||||
DEBUG("gnrc_netdev2: starting thread\n"); |
||||
|
||||
gnrc_netdev2_t *gnrc_netdev2 = (gnrc_netdev2_t*) args; |
||||
netdev2_t *dev = gnrc_netdev2->dev; |
||||
|
||||
gnrc_netdev2->pid = thread_getpid(); |
||||
|
||||
gnrc_netapi_opt_t *opt; |
||||
int res; |
||||
msg_t msg, reply, msg_queue[NETDEV2_NETAPI_MSG_QUEUE_SIZE]; |
||||
|
||||
/* setup the MAC layers message queue */ |
||||
msg_init_queue(msg_queue, NETDEV2_NETAPI_MSG_QUEUE_SIZE); |
||||
|
||||
/* register the event callback with the device driver */ |
||||
dev->event_callback = _event_cb; |
||||
dev->isr_arg = (void*) gnrc_netdev2; |
||||
|
||||
/* register the device to the network stack*/ |
||||
gnrc_netif_add(thread_getpid()); |
||||
|
||||
/* initialize low-level driver */ |
||||
dev->driver->init(dev); |
||||
|
||||
/* start the event loop */ |
||||
while (1) { |
||||
DEBUG("gnrc_netdev2: waiting for incoming messages\n"); |
||||
msg_receive(&msg); |
||||
/* dispatch NETDEV and NETAPI messages */ |
||||
switch (msg.type) { |
||||
case NETDEV2_MSG_TYPE_EVENT: |
||||
DEBUG("gnrc_netdev2: GNRC_NETDEV_MSG_TYPE_EVENT received\n"); |
||||
dev->driver->isr(dev); |
||||
break; |
||||
case GNRC_NETAPI_MSG_TYPE_SND: |
||||
DEBUG("gnrc_netdev2: GNRC_NETAPI_MSG_TYPE_SND received\n"); |
||||
gnrc_pktsnip_t *pkt = (gnrc_pktsnip_t *)msg.content.ptr; |
||||
gnrc_netdev2->send(gnrc_netdev2, pkt); |
||||
break; |
||||
case GNRC_NETAPI_MSG_TYPE_SET: |
||||
/* read incoming options */ |
||||
opt = (gnrc_netapi_opt_t *)msg.content.ptr; |
||||
DEBUG("gnrc_netdev2: GNRC_NETAPI_MSG_TYPE_SET received. opt=%s\n", |
||||
netopt2str(opt->opt)); |
||||
/* set option for device driver */ |
||||
res = dev->driver->set(dev, opt->opt, opt->data, opt->data_len); |
||||
DEBUG("gnrc_netdev2: response of netdev->set: %i\n", res); |
||||
/* send reply to calling thread */ |
||||
reply.type = GNRC_NETAPI_MSG_TYPE_ACK; |
||||
reply.content.value = (uint32_t)res; |
||||
msg_reply(&msg, &reply); |
||||
break; |
||||
case GNRC_NETAPI_MSG_TYPE_GET: |
||||
/* read incoming options */ |
||||
opt = (gnrc_netapi_opt_t *)msg.content.ptr; |
||||
DEBUG("gnrc_netdev2: GNRC_NETAPI_MSG_TYPE_GET received. opt=%s\n", |
||||
netopt2str(opt->opt)); |
||||
/* get option from device driver */ |
||||
res = dev->driver->get(dev, opt->opt, opt->data, opt->data_len); |
||||
DEBUG("gnrc_netdev2: response of netdev->get: %i\n", res); |
||||
/* send reply to calling thread */ |
||||
reply.type = GNRC_NETAPI_MSG_TYPE_ACK; |
||||
reply.content.value = (uint32_t)res; |
||||
msg_reply(&msg, &reply); |
||||
break; |
||||
default: |
||||
DEBUG("gnrc_netdev2: Unknown command %" PRIu16 "\n", msg.type); |
||||
break; |
||||
} |
||||
} |
||||
/* never reached */ |
||||
return NULL; |
||||
} |
||||
|
||||
kernel_pid_t gnrc_netdev2_init(char *stack, int stacksize, char priority, |
||||
const char *name, gnrc_netdev2_t *gnrc_netdev2) |
||||
{ |
||||
kernel_pid_t res; |
||||
|
||||
/* check if given netdev device is defined and the driver is set */ |
||||
if (gnrc_netdev2 == NULL || gnrc_netdev2->dev == NULL) { |
||||
return -ENODEV; |
||||
} |
||||
|
||||
/* create new gnrc_netdev2 thread */ |
||||
res = thread_create(stack, stacksize, priority, CREATE_STACKTEST, |
||||
_gnrc_netdev2_thread, (void *)gnrc_netdev2, name); |
||||
if (res <= 0) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
return res; |
||||
} |
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 net |
||||
* @file |
||||
* @brief gnrc netdev2 ethernet glue code |
||||
* |
||||
* @author Kaspar Schleiser <kaspar@schleiser.de> |
||||
* @} |
||||
*/ |
||||
|
||||
#include "net/gnrc.h" |
||||
#include "net/gnrc/gnrc_netdev2.h" |
||||
#include "net/ethernet/hdr.h" |
||||
|
||||
#include "od.h" |
||||
|
||||
#define ENABLE_DEBUG (0) |
||||
#include "debug.h" |
||||
|
||||
static gnrc_pktsnip_t *_recv(gnrc_netdev2_t *gnrc_netdev2) |
||||
{ |
||||
netdev2_t *dev = gnrc_netdev2->dev; |
||||
int bytes_expected = dev->driver->recv(dev, NULL, 0); |
||||
gnrc_pktsnip_t *pkt = NULL; |
||||
|
||||
if (bytes_expected) { |
||||
pkt = gnrc_pktbuf_add(NULL, NULL, |
||||
bytes_expected, |
||||
GNRC_NETTYPE_UNDEF); |
||||
|
||||
if(!pkt) { |
||||
DEBUG("_recv_ethernet_packet: cannot allocate pktsnip.\n"); |
||||
goto out; |
||||
} |
||||
|
||||
int nread = dev->driver->recv(dev, pkt->data, bytes_expected); |
||||
if(nread <= 0) { |
||||
DEBUG("_recv_ethernet_packet: read error.\n"); |
||||
goto safe_out; |
||||
} |
||||
|
||||
if (nread < bytes_expected) { |
||||
/* we've got less then the expected packet size,
|
||||
* so free the unused space.*/ |
||||
|
||||
DEBUG("_recv_ethernet_packet: reallocating.\n"); |
||||
gnrc_pktbuf_realloc_data(pkt, nread); |
||||
} |
||||
|
||||
/* mark ethernet header */ |
||||
gnrc_pktsnip_t *eth_hdr = gnrc_pktbuf_mark(pkt, sizeof(ethernet_hdr_t), GNRC_NETTYPE_UNDEF); |
||||
if (!eth_hdr) { |
||||
DEBUG("gnrc_netdev2_eth: no space left in packet buffer\n"); |
||||
goto safe_out; |
||||
} |
||||
|
||||
ethernet_hdr_t *hdr = (ethernet_hdr_t *)eth_hdr->data; |
||||
|
||||
/* set payload type from ethertype */ |
||||
pkt->type = gnrc_nettype_from_ethertype(byteorder_ntohs(hdr->type)); |
||||
|
||||
/* create netif header */ |
||||
gnrc_pktsnip_t *netif_hdr; |
||||
netif_hdr = gnrc_pktbuf_add(NULL, NULL, |
||||
sizeof(gnrc_netif_hdr_t) + (2 * ETHERNET_ADDR_LEN), |
||||
GNRC_NETTYPE_NETIF); |
||||
|
||||
if (netif_hdr == NULL) { |
||||
DEBUG("gnrc_netdev2_eth: no space left in packet buffer\n"); |
||||
pkt = eth_hdr; |
||||
goto safe_out; |
||||
} |
||||
|
||||
gnrc_netif_hdr_init(netif_hdr->data, ETHERNET_ADDR_LEN, ETHERNET_ADDR_LEN); |
||||
gnrc_netif_hdr_set_src_addr(netif_hdr->data, hdr->src, ETHERNET_ADDR_LEN); |
||||
gnrc_netif_hdr_set_dst_addr(netif_hdr->data, hdr->dst, ETHERNET_ADDR_LEN); |
||||
((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid = thread_getpid(); |
||||
|
||||
DEBUG("gnrc_netdev2_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], nread); |
||||
#if defined(MODULE_OD) && ENABLE_DEBUG |
||||
od_hex_dump(hdr, nread, OD_WIDTH_DEFAULT); |
||||
#endif |
||||
|
||||
gnrc_pktbuf_remove_snip(pkt, eth_hdr); |
||||
LL_APPEND(pkt, netif_hdr); |
||||
} |
||||
|
||||
out: |
||||
return pkt; |
||||
|
||||
safe_out: |
||||
gnrc_pktbuf_release(pkt); |
||||
return NULL; |
||||
} |
||||
|
||||
static inline void _addr_set_broadcast(uint8_t *dst) |
||||
{ |
||||
memset(dst, 0xff, ETHERNET_ADDR_LEN); |
||||
} |
||||
|
||||
#define _IPV6_DST_OFFSET (36) /* sizeof(ipv6_hdr_t) - 4 */ |
||||
|
||||
static inline void _addr_set_multicast(uint8_t *dst, gnrc_pktsnip_t *payload) |
||||
{ |
||||
switch (payload->type) { |
||||
#ifdef MODULE_IPV6 |
||||
case GNRC_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 gnrc_ipv6_hdr_t got merged */ |
||||
break; |
||||
#endif |
||||
default: |
||||
_addr_set_broadcast(dst); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static int _send(gnrc_netdev2_t *gnrc_netdev2, gnrc_pktsnip_t *pkt) |
||||
{ |
||||
ethernet_hdr_t hdr; |
||||
gnrc_netif_hdr_t *netif_hdr; |
||||
gnrc_pktsnip_t *payload; |
||||
|
||||
netdev2_t *dev = gnrc_netdev2->dev; |
||||
|
||||
if (pkt == NULL) { |
||||
DEBUG("gnrc_netdev2_eth: pkt was NULL"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
payload = pkt->next; |
||||
|
||||
if (pkt->type != GNRC_NETTYPE_NETIF) { |
||||
DEBUG("gnrc_netdev2_eth: First header was not generic netif header\n"); |
||||
return -EBADMSG; |
||||
} |
||||
|
||||
if (payload) { |
||||
hdr.type = byteorder_htons(gnrc_nettype_to_ethertype(payload->type)); |
||||
} |
||||
else { |
||||
hdr.type = byteorder_htons(ETHERTYPE_UNKNOWN); |
||||
} |
||||
|
||||
netif_hdr = pkt->data; |
||||
|
||||
/* set ethernet header */ |
||||
if (netif_hdr->src_l2addr_len == ETHERNET_ADDR_LEN) { |
||||
memcpy(hdr.dst, gnrc_netif_hdr_get_src_addr(netif_hdr), |
||||
netif_hdr->src_l2addr_len); |
||||
} |
||||
else { |
||||
dev->driver->get(dev, NETOPT_ADDRESS, hdr.src, ETHERNET_ADDR_LEN); |
||||
} |
||||
|
||||
if (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_BROADCAST) { |
||||
_addr_set_broadcast(hdr.dst); |
||||
} |
||||
else if (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_MULTICAST) { |
||||
_addr_set_multicast(hdr.dst, payload); |
||||
} |
||||
else if (netif_hdr->dst_l2addr_len == ETHERNET_ADDR_LEN) { |
||||
memcpy(hdr.dst, gnrc_netif_hdr_get_dst_addr(netif_hdr), |
||||
ETHERNET_ADDR_LEN); |
||||
} |
||||
else { |
||||
DEBUG("gnrc_netdev2_eth: destination address had unexpected format\n"); |
||||
return -EBADMSG; |
||||
} |
||||
|
||||
DEBUG("gnrc_netdev2_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]); |
||||
|
||||
size_t n; |
||||
pkt = gnrc_pktbuf_get_iovec(pkt, &n); |
||||
struct iovec *vector = (struct iovec *)pkt->data; |
||||
vector[0].iov_base = (char*)&hdr; |
||||
vector[0].iov_len = sizeof(ethernet_hdr_t); |
||||
dev->driver->send(dev, vector, n); |
||||
|
||||
gnrc_pktbuf_release(pkt); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int gnrc_netdev2_eth_init(gnrc_netdev2_t *gnrc_netdev2, netdev2_t *dev) |
||||
{ |
||||
gnrc_netdev2->send = _send; |
||||
gnrc_netdev2->recv = _recv; |
||||
gnrc_netdev2->dev = dev; |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue