1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

sys: net: gnrc: add basic netdev2 support

This commit is contained in:
Kaspar Schleiser 2015-08-21 16:06:57 +02:00
parent 076e9db374
commit 78b4bf6f58
9 changed files with 575 additions and 0 deletions

View File

@ -276,6 +276,10 @@ ifneq (,$(filter gnrc,$(USEMODULE)))
USEMODULE += gnrc_pktbuf
endif
ifneq (,$(filter gnrc_netdev2,$(USEMODULE)))
USEMODULE += netopt
endif
ifneq (,$(filter hih6130,$(USEMODULE)))
USEMODULE += vtimer
endif

View File

@ -40,6 +40,9 @@ endif
ifneq (,$(filter nhdp,$(USEMODULE)))
DIRS += net/routing/nhdp
endif
ifneq (,$(filter gnrc_netdev2,$(USEMODULE)))
DIRS += net/gnrc/link_layer/netdev2
endif
ifneq (,$(filter fib,$(USEMODULE)))
DIRS += net/network_layer/fib
endif

View File

@ -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 */
/** @} */

View File

@ -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 */
/** @} */

View File

@ -172,6 +172,11 @@ typedef enum {
*/
NETOPT_IS_WIRED,
/**
* @brief get a device's "type", e.g., ethernet, 802.15.4, ...
*/
NETOPT_DEVICE_TYPE,
/* add more options if needed */
/**

View File

@ -49,6 +49,7 @@ static const char *_netopt_strmap[] = {
[NETOPT_CSMA] = "NETOPT_CSMA",
[NETOPT_CSMA_RETRIES] = "NETOPT_CSMA_RETRIES",
[NETOPT_IS_WIRED] = "NETOPT_IS_WIRED",
[NETOPT_DEVICE_TYPE] = "NETOPT_DEVICE_TYPE",
[NETOPT_NUMOF] = "NETOPT_NUMOF",
};

View File

@ -0,0 +1,3 @@
MODULE = gnrc_netdev2
include $(RIOTBASE)/Makefile.base

View File

@ -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;
}

View File

@ -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;
}