/* * Copyright (C) 2014 Freie Universität Berlin * 2015 Kaspar Schleiser * * 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 drivers_cc110x * @{ * @file * @brief Implementation of netdev2 interface for cc110x * * @author Fabian Nack * @author Kaspar Schleiser * @} */ #include #include #include #include #include "cc110x.h" #include "cc110x-netdev2.h" #include "cc110x-internal.h" #include "cc110x-interface.h" #include "net/eui64.h" #include "periph/cpuid.h" #include "periph/gpio.h" #include "net/netdev2.h" #include "net/gnrc/nettype.h" #define ENABLE_DEBUG (0) #include "debug.h" static int _send(netdev2_t *dev, const struct iovec *vector, unsigned count) { DEBUG("%s:%u\n", __func__, __LINE__); netdev2_cc110x_t *netdev2_cc110x = (netdev2_cc110x_t*) dev; cc110x_pkt_t *cc110x_pkt = vector[0].iov_base; return cc110x_send(&netdev2_cc110x->cc110x, cc110x_pkt); } static int _recv(netdev2_t *dev, void *buf, size_t len, void *info) { DEBUG("%s:%u\n", __func__, __LINE__); cc110x_t *cc110x = &((netdev2_cc110x_t*) dev)->cc110x; cc110x_pkt_t *cc110x_pkt = &cc110x->pkt_buf.packet; if (cc110x_pkt->length > len) { return -ENOSPC; } memcpy(buf, (void*)cc110x_pkt, cc110x_pkt->length); if (info != NULL) { netdev2_cc110x_rx_info_t *cc110x_info = info; cc110x_info->rssi = cc110x->pkt_buf.rssi; cc110x_info->lqi = cc110x->pkt_buf.lqi; } return cc110x_pkt->length; } static inline int _get_iid(netdev2_t *netdev, eui64_t *value, size_t max_len) { cc110x_t *cc110x = &((netdev2_cc110x_t*) netdev)->cc110x; uint8_t *eui64 = (uint8_t*) value; if (max_len < sizeof(eui64_t)) { return -EOVERFLOW; } /* make address compatible to https://tools.ietf.org/html/rfc6282#section-3.2.2*/ memset(eui64, 0, sizeof(eui64_t)); eui64[3] = 0xff; eui64[4] = 0xfe; eui64[7] = cc110x->radio_address; return sizeof(eui64_t); } static int _get(netdev2_t *dev, netopt_t opt, void *value, size_t value_len) { cc110x_t *cc110x = &((netdev2_cc110x_t*) dev)->cc110x; switch (opt) { case NETOPT_DEVICE_TYPE: assert(value_len == 2); *((uint16_t *) value) = NETDEV2_TYPE_CC110X; return 2; case NETOPT_PROTO: assert(value_len == sizeof(gnrc_nettype_t)); *((gnrc_nettype_t *)value) = cc110x->proto; return sizeof(gnrc_nettype_t); case NETOPT_CHANNEL: assert(value_len > 1); *((uint16_t *)value) = (uint16_t)cc110x->radio_channel; return 2; case NETOPT_ADDRESS: assert(value_len > 0); *((uint8_t *)value) = cc110x->radio_address; return 1; case NETOPT_MAX_PACKET_SIZE: assert(value_len > 0); *((uint8_t *)value) = CC110X_PACKET_LENGTH; return 1; case NETOPT_IPV6_IID: return _get_iid(dev, value, value_len); default: break; } return -ENOTSUP; } static int _set(netdev2_t *dev, netopt_t opt, void *value, size_t value_len) { cc110x_t *cc110x = &((netdev2_cc110x_t*) dev)->cc110x; switch (opt) { case NETOPT_CHANNEL: { uint8_t *arg = (uint8_t*)value; uint8_t channel = arg[value_len-1]; if ((channel < CC110X_MIN_CHANNR) || (channel > CC110X_MAX_CHANNR)) { return -EINVAL; } if (cc110x_set_channel(cc110x, channel) == -1) { return -EINVAL; } return 1; } case NETOPT_ADDRESS: if (value_len < 1) { return -EINVAL; } if (!cc110x_set_address(cc110x, *(uint8_t*)value)) { return -EINVAL; } return 1; case NETOPT_PROTO: if (value_len != sizeof(gnrc_nettype_t)) { return -EINVAL; } else { cc110x->proto = (gnrc_nettype_t) value; return sizeof(gnrc_nettype_t); } break; default: return -ENOTSUP; } return 0; } static void _netdev2_cc110x_isr(void *arg) { netdev2_t *netdev2 = (netdev2_t*) arg; netdev2->event_callback(netdev2, NETDEV2_EVENT_ISR); } static void _netdev2_cc110x_rx_callback(void *arg) { netdev2_t *netdev2 = (netdev2_t*) arg; cc110x_t *cc110x = &((netdev2_cc110x_t*) arg)->cc110x; gpio_irq_disable(cc110x->params.gdo2); netdev2->event_callback(netdev2, NETDEV2_EVENT_RX_COMPLETE); } static void _isr(netdev2_t *dev) { cc110x_t *cc110x = &((netdev2_cc110x_t*) dev)->cc110x; cc110x_isr_handler(cc110x, _netdev2_cc110x_rx_callback, (void*)dev); } static int _init(netdev2_t *dev) { DEBUG("%s:%u\n", __func__, __LINE__); cc110x_t *cc110x = &((netdev2_cc110x_t*) dev)->cc110x; gpio_init_int(cc110x->params.gdo2, GPIO_IN, GPIO_BOTH, &_netdev2_cc110x_isr, (void*)dev); gpio_set(cc110x->params.gdo2); gpio_irq_disable(cc110x->params.gdo2); /* Switch to RX mode */ cc110x_rd_set_mode(cc110x, RADIO_MODE_ON); return 0; } const netdev2_driver_t netdev2_cc110x_driver = { .send=_send, .recv=_recv, .init=_init, .get=_get, .set=_set, .isr=_isr }; int netdev2_cc110x_setup(netdev2_cc110x_t *netdev2_cc110x, const cc110x_params_t *params) { DEBUG("netdev2_cc110x_setup()\n"); netdev2_cc110x->netdev.driver = &netdev2_cc110x_driver; /* set default protocol */ #ifdef MODULE_GNRC_NETIF # ifdef MODULE_GNRC_SIXLOWPAN netdev2_cc110x->cc110x.proto = GNRC_NETTYPE_SIXLOWPAN; # else netdev2_cc110x->cc110x.proto = GNRC_NETTYPE_UNDEF; # endif #endif return cc110x_setup(&netdev2_cc110x->cc110x, params); }