diff --git a/sys/Makefile b/sys/Makefile index 2461904134..beba5a7217 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -16,6 +16,9 @@ endif ifneq (,$(filter l2_ping,$(USEMODULE))) DIRS += net/link_layer/ping endif +ifneq (,$(filter nomac,$(USEMODULE))) + DIRS += net/link_layer/nomac +endif ifneq (,$(filter transport_layer,$(USEMODULE))) USEMODULE += udp USEMODULE += tcp diff --git a/sys/Makefile.include b/sys/Makefile.include index fa45959152..d56b9809e7 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -1,3 +1,6 @@ +ifneq (,$(filter nomac,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include +endif ifneq (,$(filter transport_layer,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include endif diff --git a/sys/auto_init/Makefile b/sys/auto_init/Makefile index d0a6370718..6579f361bc 100644 --- a/sys/auto_init/Makefile +++ b/sys/auto_init/Makefile @@ -2,4 +2,8 @@ ifneq (,$(filter net_if,$(USEMODULE))) INCLUDES += -I$(RIOTBASE)/sys/net/include/ endif +ifneq (,$(filter nomac,$(USEMODULE))) + INCLUDES += -I$(RIOTBASE)/sys/net/include/ +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 86cbaf7acd..df52d0c97a 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -67,6 +67,10 @@ #include "tcp.h" #endif +#ifdef MODULE_NOMAC +#include "nomac.h" +#endif + #ifdef MODULE_NET_IF #include "cpu-conf.h" #include "cpu.h" @@ -241,6 +245,10 @@ void auto_init(void) DEBUG("Auto init net_if module.\n"); l2_ping_init(); #endif +#ifdef MODULE_NOMAC + DEBUG("Auto init nomac module.\n"); + nomac_init_module(); +#endif #ifdef MODULE_NET_IF DEBUG("Auto init net_if module.\n"); auto_init_net_if(); diff --git a/sys/net/include/nomac.h b/sys/net/include/nomac.h new file mode 100644 index 0000000000..8323078191 --- /dev/null +++ b/sys/net/include/nomac.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * 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 nomac Link layer with no medium access + * @ingroup net + * @brief Link layer protocol to speak with any transceiver driver + * directly without any medium access. + * @{ + * + * @file + * @brief Link layer protocol to speak with any transceiver driver + * directly without any medium access. + * + * @author Martine Lenders + */ + +#ifndef __NOMAC_H_ +#define __NOMAC_H_ + +#include + +#include "netdev/base.h" +#include "netapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef NOMAC_REGISTRY_SIZE +/** + * @brief The size of NOMAC's registry of receiving threads. + */ +#define NOMAC_REGISTRY_SIZE (1) +#endif + +/** + * @brief Recommended stack size for a NOMAC thread + * TODO: determine real minimal size based on thread_print_all() output + */ +#define NOMAC_CONTROL_STACKSIZE (KERNEL_CONF_STACKSIZE_DEFAULT) + +/** + * @brief Basic configuration types + * + * @extends netapi_conf_type_t + */ +typedef enum { + NOMAC_PROTO = NETAPI_CONF_PROTO, /**< Set or get protocol */ + NOMAC_CHANNEL = NETAPI_CONF_CARRIER, /**< Set or get channel */ + NOMAC_ADDRESS = NETAPI_CONF_ADDRESS, /**< Set or get address */ + NOMAC_NID = NETAPI_CONF_SUBNETS, /**< Set or get network id + * (e.g. PAN ID in 802.15.4)*/ + NOMAC_MAX_PACKET_SIZE = NETAPI_CONF_MAX_PACKET_SIZE, /**< Set or get maximum + * packet size */ + NOMAC_SRC_LEN = NETAPI_CONF_SRC_LEN, /**< Set or get default source length */ + NOMAC_REGISTRY = NETAPI_CONF_REGISTRY, /**< get registered threads */ + NOMAC_ADDRESS2 = NETDEV_OPT_ADDRESS_LONG, /**< Set or get alternative address + * format (e.g EUI-64 in 802.15.4) */ +} nomac_conf_type_t; + +/** + * @brief Initializes the module + */ +void nomac_init_module(void); + +/** + * @brief Initialize new NOMAC layer. + * + * @param[in] stack Stack for the control thread + * @param[in] stacksize Size of *stack* + * @param[in] priority Priority for the control thread + * @param[in] name Name for the control thread + * @param[in] dev An *initialized* network device to use with this MAC + * layer + * + * @see @ref thread_create + * + * @return PID of NOMAC control thread on success + * @return -EINVAL if priority is invalid + * @return -EOVERFLOW if no slot for the new thread is available + */ +kernel_pid_t nomac_init(char *stack, int stacksize, char priority, + const char *name, netdev_t *dev); + +#ifdef MODULE_NETDEV_DUMMY +/** + * @brief Re-updates the receive callback of a network device for testing + * purposes + * + * @param[in] dev A network device + */ +void nomac_update_callback(netdev_t *dev); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __NOMAC_H_ */ + +/** @} */ diff --git a/sys/net/link_layer/nomac/Makefile b/sys/net/link_layer/nomac/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/link_layer/nomac/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/link_layer/nomac/nomac.c b/sys/net/link_layer/nomac/nomac.c new file mode 100644 index 0000000000..aaa16492ff --- /dev/null +++ b/sys/net/link_layer/nomac/nomac.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * 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. + */ +#include +#include + +#include "netapi.h" +#include "msg.h" +#include "netdev/base.h" +#include "thread.h" + +#include "nomac.h" + +#ifdef MODULE_NETDEV_DUMMY +#include "netdev_dummy.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define NOMAC_MSG_QUEUE_SIZE (16) + +static struct { + kernel_pid_t registrar_pid; /**< Thread recipient is registered to */ + kernel_pid_t recipient_pid; /**< Registered recipient thread */ +} _nomac_registry[NOMAC_REGISTRY_SIZE]; + +static int _nomac_recv_cb(netdev_t *dev, void *src, size_t src_len, void *dest, + size_t dest_len, void *payload, size_t payload_len) +{ + (void)dev; + kernel_pid_t current_pid = thread_getpid(); + size_t offset; + netapi_rcv_pkt_t packet; + netapi_ack_t ack_mem; + msg_t msg_pkt, msg_ack; + + packet.type = NETAPI_CMD_RCV; + packet.ack = &ack_mem; + packet.src = src; + packet.src_len = src_len; + packet.dest = dest; + packet.dest_len = dest_len; + msg_pkt.type = NETAPI_MSG_TYPE; + msg_pkt.content.ptr = (char *)(&packet); + + for (unsigned int i = 0; i < NOMAC_REGISTRY_SIZE; i++) { + if (_nomac_registry[i].registrar_pid == current_pid) { + offset = 0; + + while (offset < payload_len) { + netapi_ack_t *ack; + packet.data = (void *)(((char *)payload) + offset); + packet.data_len = payload_len - offset; + + msg_send_receive(&msg_pkt, &msg_ack, + _nomac_registry[i].recipient_pid); + ack = (netapi_ack_t *)(msg_ack.content.ptr); + + if ((msg_ack.type == NETAPI_MSG_TYPE) && + (ack->type == NETAPI_CMD_ACK) && + (ack->orig == NETAPI_CMD_RCV)) { + if (ack->result > 0) { + offset += (ack->result); + } + else { + DEBUG("Error code received for registrar %" PRIkernel_pid + " with recipient %" PRIkernel_pid ": %d", + _nomac_registry[i].registrar_pid, + _nomac_registry[i].recipient_pid, + -(ack->result)); + + return ack->result; + } + } + else { + DEBUG("Unexpected msg instead of ACK. Abort for registrar " + "\"%s\" with recipient \"%s\": msg.type = %d, " + "ack->type = %d, ack->orig = %d", + thread_getname(_nomac_registry[i].registrar_pid), + thread_getname(_nomac_registry[i].recipient_pid), + msg_ack.type, ack->type, ack->orig); + + return -ENOMSG; + } + + } + } + } + + return payload_len; +} + +static inline int _nomac_send(netdev_t *dev, netapi_snd_pkt_t *pkt) +{ + return dev->driver->send_data(dev, pkt->dest, pkt->dest_len, pkt->ulh, + pkt->data, pkt->data_len); +} + +static int _nomac_get_registry(netapi_conf_t *conf) +{ + kernel_pid_t current_pid = thread_getpid(); + kernel_pid_t registry[NOMAC_REGISTRY_SIZE]; + uint8_t size = 0; + + for (int i = 0; i < NOMAC_REGISTRY_SIZE; i++) { + if ((size * sizeof(kernel_pid_t)) > (conf->data_len)) { + return -EOVERFLOW; + } + + if (_nomac_registry[i].registrar_pid == current_pid) { + registry[size++] = _nomac_registry[i].recipient_pid; + } + } + + conf->data_len = size * sizeof(kernel_pid_t); + memcpy(conf->data, registry, conf->data_len); + + return 0; +} + +static int _nomac_get_option(netdev_t *dev, netapi_conf_t *conf) +{ + int res; + + switch ((nomac_conf_type_t)conf->param) { + case NOMAC_PROTO: + case NOMAC_CHANNEL: + case NOMAC_ADDRESS: + case NOMAC_NID: + case NOMAC_MAX_PACKET_SIZE: + case NOMAC_ADDRESS2: + if ((res = dev->driver->get_option(dev, (netdev_opt_t)conf->param, + conf->data, &(conf->data_len)) + ) == 0) { + return (int)conf->data_len; + } + else { + return res; + } + + case NOMAC_REGISTRY: + return _nomac_get_registry(conf); + + default: + break; + } + + return -ENOTSUP; +} + +static int _nomac_set_option(netdev_t *dev, netapi_conf_t *conf) +{ + switch ((nomac_conf_type_t)(conf->param)) { + case NOMAC_PROTO: + case NOMAC_CHANNEL: + case NOMAC_ADDRESS: + case NOMAC_NID: + case NOMAC_ADDRESS2: + return dev->driver->set_option(dev, (netdev_opt_t)conf->param, + conf->data, conf->data_len); + + default: + break; + } + + return -ENOTSUP; +} + +static void *_nomac_runner(void *args) +{ + netdev_t *dev = (netdev_t *)args; + msg_t msg_cmd, msg_ack, msg_queue[NOMAC_MSG_QUEUE_SIZE]; + + netapi_cmd_t *cmd; + netapi_ack_t *ack; + + msg_ack.type = NETAPI_MSG_TYPE; + + msg_init_queue(msg_queue, NOMAC_MSG_QUEUE_SIZE); + + dev->driver->init(dev); + dev->driver->add_receive_data_callback(dev, _nomac_recv_cb); + + while (1) { + msg_receive(&msg_cmd); + + if (msg_cmd.type == NETDEV_MSG_EVENT_TYPE) { + dev->driver->event(dev, msg_cmd.content.value); + } + else if (msg_cmd.type == NETAPI_MSG_TYPE) { + cmd = (netapi_cmd_t *)(msg_cmd.content.ptr); + ack = cmd->ack; + msg_ack.content.ptr = (char *)ack; + + switch (cmd->type) { + case NETAPI_CMD_SND: + ack->result = _nomac_send(dev, (netapi_snd_pkt_t *)cmd); + break; + + case NETAPI_CMD_GET: + ack->result = _nomac_get_option(dev, (netapi_conf_t *)cmd); + break; + + case NETAPI_CMD_SET: + ack->result = _nomac_set_option(dev, (netapi_conf_t *)cmd); + break; + + case NETAPI_CMD_REG: + ack->result = -ENOBUFS; + + for (int i = 0; i < NOMAC_REGISTRY_SIZE; i++) { + if (_nomac_registry[i].registrar_pid == KERNEL_PID_UNDEF) { + netapi_reg_t *reg = (netapi_reg_t *)cmd; + + _nomac_registry[i].registrar_pid = thread_getpid(); + _nomac_registry[i].recipient_pid = reg->reg_pid; + ack->result = NETAPI_STATUS_OK; + + break; + } + } + + break; + + case NETAPI_CMD_UNREG: + ack->result = NETAPI_STATUS_OK; + + for (int i = 0; i < NOMAC_REGISTRY_SIZE; i++) { + netapi_reg_t *reg = (netapi_reg_t *)cmd; + + if (_nomac_registry[i].registrar_pid == thread_getpid() && + _nomac_registry[i].recipient_pid == reg->reg_pid) { + _nomac_registry[i].recipient_pid = KERNEL_PID_UNDEF; + + break; + } + + } + + break; + +#ifdef MODULE_NETDEV_DUMMY + + case NETAPI_CMD_FIRE_RCV: + ack->result = unittest_netdev_dummy_fire_rcv_event(dev, + ((netapi_rcv_pkt_t *)cmd)->src, + ((netapi_rcv_pkt_t *)cmd)->src_len, + ((netapi_rcv_pkt_t *)cmd)->dest, + ((netapi_rcv_pkt_t *)cmd)->dest_len, + ((netapi_rcv_pkt_t *)cmd)->data, + ((netapi_rcv_pkt_t *)cmd)->data_len); + break; +#endif + + default: + ack->result = -ENOTSUP; + break; + } + + ack->type = NETAPI_CMD_ACK; + ack->orig = cmd->type; + msg_reply(&msg_cmd, &msg_ack); + } + } + + /* never reached */ + return NULL; +} + +void nomac_init_module(void) +{ + for (int i = 0; i < NOMAC_REGISTRY_SIZE; i++) { + _nomac_registry[i].registrar_pid = KERNEL_PID_UNDEF; + _nomac_registry[i].recipient_pid = KERNEL_PID_UNDEF; + } +} + +kernel_pid_t nomac_init(char *stack, int stacksize, char priority, + const char *name, netdev_t *dev) +{ + return thread_create(stack, stacksize, priority, CREATE_STACKTEST, + _nomac_runner, (void *)dev, name); +} + +#ifdef MODULE_NETDEV_DUMMY +void nomac_update_callback(netdev_t *dev) +{ + dev->driver->add_receive_data_callback(dev, _nomac_recv_cb); +} +#endif