Browse Source

drivers: add cc110x driver

dev/timer
Kaspar Schleiser 8 years ago
parent
commit
14d0ef6c4d
  1. 4
      Makefile.dep
  2. 3
      drivers/cc110x/Makefile
  3. 100
      drivers/cc110x/cc110x-defaultsettings.c
  4. 218
      drivers/cc110x/cc110x-netdev2.c
  5. 298
      drivers/cc110x/cc110x-rxtx.c
  6. 179
      drivers/cc110x/cc110x-spi.c
  7. 281
      drivers/cc110x/cc110x.c
  8. 1
      drivers/cc110x/gnrc_cc110x/Makefile
  9. 192
      drivers/cc110x/gnrc_cc110x/gnrc_netdev2_cc110x.c
  10. 132
      drivers/include/cc110x.h
  11. 46
      drivers/include/cc110x/cc110x-defaultsettings.h
  12. 207
      drivers/include/cc110x/cc110x-defines.h
  13. 66
      drivers/include/cc110x/cc110x-interface.h
  14. 208
      drivers/include/cc110x/cc110x-internal.h
  15. 62
      drivers/include/cc110x/cc110x-netdev2.h
  16. 116
      drivers/include/cc110x/cc110x-spi.h
  17. 45
      drivers/include/cc110x/gnrc_netdev2_cc110x.h
  18. 5
      sys/auto_init/auto_init.c
  19. 70
      sys/auto_init/netif/auto_init_cc110x.c

4
Makefile.dep

@ -36,6 +36,10 @@ ifneq (,$(filter at86rf2%,$(USEMODULE)))
USEMODULE += netif
endif
ifneq (,$(filter cc110x,$(USEMODULE)))
USEMODULE += ieee802154
endif
ifneq (,$(filter kw2xrf,$(USEMODULE)))
USEMODULE += ieee802154
USEMODULE += netif

3
drivers/cc110x/Makefile

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

100
drivers/cc110x/cc110x-defaultsettings.c

@ -0,0 +1,100 @@
/*
* Copyright (C) 2013 INRIA
* 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 drivers_cc110x
* @{
*
* @file
* @brief TI Chipcon CC110x default settings
*
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include "board.h"
#include "cc110x.h"
/**
* @brief PATABLE with available output powers
* @note If changed in size, adjust MAX_OUTPUT_POWER definition
* in CC110x interface
*/
const char cc110x_default_pa_table[8] = {
0x00, /*< -52 dBm */
0x0D, /*< -20 dBm */
0x34, /*< -10 dBm */
0x57, /*< - 5 dBm */
0x8E, /*< 0 dBm */
0x85, /*< + 5 dBm */
0xCC, /*< + 7 dBm */
0xC3 /*< +10 dBm */
};
const char cc110x_default_base_freq[3] = { 0x21, 0x71, 0x7F };
/**
* @brief cc110x default settings
*/
const char cc110x_default_conf[] = {
0x06, /* IOCFG2 */
0x2E, /* IOCFG1 */
/* some boards use cc110x' GDO0 as clock source, so for those, we allow
* overriding of the corresponding setting, e.g., in board.h */
#ifdef CC110X_IOCONF0_VAL
CC110X_IOCONF0_VAL,
#else
0x0E, /* IOCFG0 */
#endif
0x07, /* FIFOTHR */
0x9B, /* SYNC1 */
0xAD, /* SYNC0 */
0xFF, /* PKTLEN */
0x06, /* PKTCTRL1 */
0x45, /* PKTCTRL0 (variable packet length) */
0xFF, /* ADDR */
0x00, /* CHANNR */
0x0F, /* FSCTRL1 */
0x00, /* FSCTRL0 */
0x21, /* FREQ2 */
0x71, /* FREQ1 */
0x7A, /* FREQ0 */
0x7C, /* MDMCFG4 */
0x7A, /* MDMCFG3 */
0x06, /* MDMCFG2 */
0xC0, /* MDMCFG1 */
0xF8, /* MDMCFG0 */
0x44, /* DEVIATN */
0x07, /* MCSM2 */
0x03, /* MCSM1 */
0x18, /* MCSM0 */
0x16, /* FOCCFG */
0x6C, /* BSCFG */
0x45, /* AGCCTRL2 */
0x40, /* AGCCTRL1 */
0x91, /* AGCCTRL0 */
0x87, /* WOREVT1 */
0x6B, /* WOREVT0 */
0xF8, /* WORCTRL */
0x56, /* FREND1 */
0x17, /* FREND0 */
0xEA, /* FSCAL3 */
0x2A, /* FSCAL2 */
0x00, /* FSCAL1 */
0x1F, /* FSCAL0 */
0x00 /* padding to 4 bytes */
};
/**
* @brief The size of the configuration array for CC110X in bytes
* */
const uint8_t cc110x_default_conf_size = sizeof(cc110x_default_conf);

218
drivers/cc110x/cc110x-netdev2.c

@ -0,0 +1,218 @@
/*
* Copyright (C) 2014 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 drivers_cc110x
* @{
* @file
* @brief Implementation of netdev2 interface for cc110x
*
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "cc110x.h"
#include "cc110x/cc110x-netdev2.h"
#include "cc110x/cc110x-internal.h"
#include "cc110x/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, int 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, char* buf, int len)
{
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);
return cc110x_pkt->length;
}
static inline int _get_iid(netdev2_t *netdev, eui64_t *value, size_t max_len)
{
if (max_len < sizeof(eui64_t)) {
return -EOVERFLOW;
}
uint8_t *eui64 = (uint8_t*) value;
#ifdef CPUID_ID_LEN
int n = (CPUID_ID_LEN < sizeof(eui64_t))
? CPUID_ID_LEN
: sizeof(eui64_t);
char cpuid[CPUID_ID_LEN];
cpuid_get(cpuid);
memcpy(eui64 + 8 - n, cpuid, n);
#else
for (int i = 0; i < 8; i++) {
eui64[i] = i;
}
#endif
/* make sure we mark the address as non-multicast and not globally unique */
eui64[0] &= ~(0x01);
eui64[0] |= 0x02;
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));
#ifdef MODULE_GNRC_SIXLOWPAN
*((gnrc_nettype_t*)value) = GNRC_NETTYPE_SIXLOWPAN;
#else
*((gnrc_nettype_t*)value) = GNRC_NETTYPE_UNDEF;
#endif
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;
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, netdev2->isr_arg);
}
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, netdev2->isr_arg);
}
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_NOPULL, 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;
return cc110x_setup(&netdev2_cc110x->cc110x, params);
}

298
drivers/cc110x/cc110x-rxtx.c

@ -0,0 +1,298 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
*
* 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 Functions for packet reception and transmission on cc110x devices
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "cc110x.h"
#include "cc110x/cc110x-spi.h"
#include "cc110x/cc110x-internal.h"
#include "cc110x/cc110x-interface.h"
#include "cc110x/cc110x-defines.h"
#include "periph/gpio.h"
#include "irq.h"
#include "kernel_types.h"
#include "msg.h"
#include "cpu_conf.h"
#include "cpu.h"
#include "log.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _rx_abort(cc110x_t *dev)
{
gpio_irq_disable(dev->params.gdo2);
cc110x_strobe(dev, CC110X_SIDLE); /* Switch to IDLE (should already be)... */
cc110x_strobe(dev, CC110X_SFRX); /* ...for flushing the RX FIFO */
cc110x_switch_to_rx(dev);
}
static void _rx_start(cc110x_t *dev)
{
dev->radio_state = RADIO_RX_BUSY;
cc110x_pkt_buf_t *pkt_buf = &dev->pkt_buf;
pkt_buf->pos = 0;
gpio_irq_disable(dev->params.gdo2);
cc110x_write_reg(dev, CC110X_IOCFG2, 0x01);
gpio_irq_enable(dev->params.gdo2);
}
static void _rx_read_data(cc110x_t *dev, void(*callback)(void*), void*arg)
{
int fifo = cc110x_get_reg_robust(dev, 0xfb);
if (fifo & 0x80) {
DEBUG("%s:%s:%u rx overflow\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_rx_abort(dev);
return;
}
if (!fifo) {
gpio_irq_enable(dev->params.gdo2);
return;
}
cc110x_pkt_buf_t *pkt_buf = &dev->pkt_buf;
if (!pkt_buf->pos) {
pkt_buf->pos = 1;
pkt_buf->packet.length = cc110x_read_reg(dev, CC110X_RXFIFO);
/* Possible packet received, RX -> IDLE (0.1 us) */
dev->cc110x_statistic.packets_in++;
}
int left = pkt_buf->packet.length+1 - pkt_buf->pos;
/* if the fifo doesn't contain the rest of the packet,
* leav at least one byte as per spec sheet. */
int to_read = (fifo < left) ? (fifo-1) : fifo;
if (to_read > left) {
to_read = left;
}
if (to_read) {
cc110x_readburst_reg(dev, CC110X_RXFIFO,
((char *)&pkt_buf->packet)+pkt_buf->pos, to_read);
pkt_buf->pos += to_read;
}
if (to_read == left) {
uint8_t status[2];
/* full packet received. */
/* Read the 2 appended status bytes (status[0] = RSSI, status[1] = LQI) */
cc110x_readburst_reg(dev, CC110X_RXFIFO, (char *)status, 2);
/* Store RSSI value of packet */
pkt_buf->rssi = status[I_RSSI];
/* Bit 0-6 of LQI indicates the link quality (LQI) */
pkt_buf->lqi = status[I_LQI] & LQI_EST;
/* MSB of LQI is the CRC_OK bit */
int crc_ok = (status[I_LQI] & CRC_OK) >> 7;
if (crc_ok) {
LOG_DEBUG("cc110x: received packet from=%u to=%u payload "
"len=%u\n",
(unsigned)pkt_buf->packet.phy_src,
(unsigned)pkt_buf->packet.address,
pkt_buf->packet.length-3);
/* let someone know that we've got a packet */
callback(arg);
cc110x_switch_to_rx(dev);
}
else {
DEBUG("%s:%s:%u crc-error\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
dev->cc110x_statistic.packets_in_crc_fail++;
_rx_abort(dev);
}
}
}
static void _rx_continue(cc110x_t *dev, void(*callback)(void*), void*arg)
{
if (dev->radio_state != RADIO_RX_BUSY) {
DEBUG("%s:%s:%u _rx_continue in invalid state\n", RIOT_FILE_RELATIVE,
__func__, __LINE__);
_rx_abort(dev);
return;
}
gpio_irq_disable(dev->params.gdo2);
do {
_rx_read_data(dev, callback, arg);
}
while (gpio_read(dev->params.gdo2));
}
static void _tx_abort(cc110x_t *dev)
{
cc110x_switch_to_rx(dev);
}
static void _tx_continue(cc110x_t *dev)
{
gpio_irq_disable(dev->params.gdo2);
cc110x_pkt_t *pkt = &dev->pkt_buf.packet;
int size = pkt->length + 1;
int left = size - dev->pkt_buf.pos;
if (!left) {
dev->cc110x_statistic.raw_packets_out++;
LOG_DEBUG("cc110x: packet successfully sent.\n");
cc110x_switch_to_rx(dev);
return;
}
int fifo = 64 - cc110x_get_reg_robust(dev, 0xfa);
if (fifo & 0x80) {
DEBUG("%s:%s:%u tx underflow!\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_tx_abort(dev);
return;
}
if (!fifo) {
DEBUG("%s:%s:%u fifo full!?\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_tx_abort(dev);
return;
}
int to_send = left > fifo ? fifo : left;
/* Write packet into TX FIFO */
cc110x_writeburst_reg(dev, CC110X_TXFIFO, ((char *)pkt)+dev->pkt_buf.pos, to_send);
dev->pkt_buf.pos += to_send;
if (left == size) {
/* Switch to TX mode */
cc110x_strobe(dev, CC110X_STX);
}
if (to_send < left) {
/* set GDO2 to 0x2 -> will deassert at TX FIFO below threshold */
gpio_irq_enable(dev->params.gdo2);
cc110x_write_reg(dev, CC110X_IOCFG2, 0x02);
}
else {
/* set GDO2 to 0x6 -> will deassert at packet end */
cc110x_write_reg(dev, CC110X_IOCFG2, 0x06);
gpio_irq_enable(dev->params.gdo2);
}
}
void cc110x_isr_handler(cc110x_t *dev, void(*callback)(void*), void*arg)
{
switch (dev->radio_state) {
case RADIO_RX:
if (gpio_read(dev->params.gdo2)) {
_rx_start(dev);
}
else {
DEBUG("cc110x_isr_handler((): isr handled too slow?\n");
_rx_abort(dev);
}
break;
case RADIO_RX_BUSY:
_rx_continue(dev, callback, arg);
break;
case RADIO_TX_BUSY:
if (!gpio_read(dev->params.gdo2)) {
_tx_continue(dev);
}
else {
DEBUG("cc110x_isr_handler() RADIO_TX_BUSY + GDO2\n");
}
break;
default:
DEBUG("%s:%s:%u: unhandled mode\n", RIOT_FILE_RELATIVE,
__func__, __LINE__);
}
}
int cc110x_send(cc110x_t *dev, cc110x_pkt_t *packet)
{
DEBUG("cc110x: snd pkt to %u payload_length=%u\n",
(unsigned)packet->address, (unsigned)packet->length-3);
uint8_t size;
switch (dev->radio_state) {
case RADIO_RX_BUSY:
case RADIO_TX_BUSY:
DEBUG("cc110x: invalid state for sending: %s\n",
cc110x_state_to_text(dev->radio_state));
return -EAGAIN;
}
/*
* Number of bytes to send is:
* length of phy payload (packet->length)
* + size of length field (1 byte)
*/
size = packet->length + 1;
if (size > CC110X_PACKET_LENGTH) {
DEBUG("%s:%s:%u trying to send oversized packet\n",
RIOT_FILE_RELATIVE, __func__, __LINE__);
return -ENOSPC;
}
/* set source address */
packet->phy_src = dev->radio_address;
/* Disable RX interrupt */
gpio_irq_disable(dev->params.gdo2);
dev->radio_state = RADIO_TX_BUSY;
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_tx();
#endif
cc110x_write_reg(dev, CC110X_IOCFG2, 0x02);
/* Put CC110x in IDLE mode to flush the FIFO */
cc110x_strobe(dev, CC110X_SIDLE);
/* Flush TX FIFO to be sure it is empty */
cc110x_strobe(dev, CC110X_SFTX);
memcpy((char*)&dev->pkt_buf.packet, packet, size);
dev->pkt_buf.pos = 0;
_tx_continue(dev);
return size;
}

179
drivers/cc110x/cc110x-spi.c

@ -0,0 +1,179 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* 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 drivers_cc110x
* @{
*
* @file
* @brief TI Chipcon CC110x spi driver
*
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Joakim Gebart <joakim.gebart@eistec.se>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <stdio.h>
#include "cc110x.h"
#include "cc110x/cc110x-spi.h"
#include "cc110x/cc110x-internal.h"
#include "cc110x/cc110x-defines.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#include "xtimer.h"
#include "irq.h"
/**********************************************************************
* CC110x spi access
**********************************************************************/
void cc110x_cs(cc110x_t *dev)
{
volatile int retry_count = 0;
/* Switch MISO/GDO1 to GPIO input mode */
#ifndef GPIO_READS_SPI_PINS
gpio_init(dev->params.gdo1, GPIO_DIR_IN, GPIO_NOPULL);
#endif
/* CS to low */
gpio_clear(dev->params.cs);
/* Wait for SO to go low (voltage regulator
* has stabilized and the crystal is running) */
while (gpio_read(dev->params.gdo1)) {
/* Wait ~500us and try again */
xtimer_usleep(CS_SO_WAIT_TIME);
if (gpio_read(dev->params.gdo1)) {
retry_count++;
if (retry_count > CC110X_GDO1_LOW_RETRY) {
puts("[CC110X spi] fatal error\n");
break;
}
gpio_set(dev->params.cs);
gpio_clear(dev->params.cs);
}
}
/* Switch MISO/GDO1 to spi mode */
#ifndef GPIO_READS_SPI_PINS
spi_conf_pins(dev->params.spi);
#endif
}
void cc110x_writeburst_reg(cc110x_t *dev, uint8_t addr, const char *src, uint8_t count)
{
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
spi_transfer_regs(dev->params.spi, addr | CC110X_WRITE_BURST, (char *)src, 0, count);
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
}
void cc110x_readburst_reg(cc110x_t *dev, uint8_t addr, char *buffer, uint8_t count)
{
int i = 0;
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
spi_transfer_byte(dev->params.spi, addr | CC110X_READ_BURST, 0);
while (i < count) {
spi_transfer_byte(dev->params.spi, CC110X_NOBYTE, &buffer[i]);
i++;
}
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
}
void cc110x_write_reg(cc110x_t *dev, uint8_t addr, uint8_t value)
{
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
spi_transfer_reg(dev->params.spi, addr, value, 0);
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
}
uint8_t cc110x_read_reg(cc110x_t *dev, uint8_t addr)
{
char result;
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
spi_transfer_reg(dev->params.spi, addr | CC110X_READ_SINGLE, CC110X_NOBYTE, &result);
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
return (uint8_t) result;
}
uint8_t cc110x_read_status(cc110x_t *dev, uint8_t addr)
{
char result;
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
spi_transfer_reg(dev->params.spi, addr | CC110X_READ_BURST, CC110X_NOBYTE, &result);
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
return (uint8_t) result;
}
uint8_t cc110x_get_reg_robust(cc110x_t *dev, uint8_t addr)
{
char result, result2;
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
do {
spi_transfer_reg(dev->params.spi, addr | CC110X_READ_BURST, CC110X_NOBYTE, &result);
spi_transfer_reg(dev->params.spi, addr | CC110X_READ_BURST, CC110X_NOBYTE, &result2);
} while (result != result2);
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
return (uint8_t) result;
}
uint8_t cc110x_strobe(cc110x_t *dev, uint8_t c)
{
#ifdef CC110X_DONT_RESET
if (c == CC110X_SRES) {
return 0;
}
#endif
char result;
unsigned int cpsr;
spi_acquire(dev->params.spi);
cpsr = disableIRQ();
cc110x_cs(dev);
spi_transfer_byte(dev->params.spi, c, &result);
gpio_set(dev->params.cs);
restoreIRQ(cpsr);
spi_release(dev->params.spi);
return (uint8_t) result;
}

281
drivers/cc110x/cc110x.c

@ -0,0 +1,281 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
* 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 drivers_cc110x
* @{
* @file
* @brief Basic functionality of cc110x driver
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include "board.h"
#include "periph/cpuid.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#include "xtimer.h"
#include "cpu.h"
#include "log.h"
#include "cc110x.h"
#include "cc110x/cc110x-defaultsettings.h"
#include "cc110x/cc110x-defines.h"
#include "cc110x/cc110x-interface.h"
#include "cc110x/cc110x-internal.h"
#include "cc110x/cc110x-spi.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* Internal function prototypes */
#ifndef CC110X_DONT_RESET
static void _reset(cc110x_t *dev);
static void _power_up_reset(cc110x_t *dev);
#endif
int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
#ifdef MODULE_CC110X_HOOKS
cc110x_hooks_init();
#endif
dev->params = *params;
/* Configure chip-select */
gpio_init(dev->params.cs, GPIO_DIR_OUT, GPIO_NOPULL);
gpio_set(dev->params.cs);
/* Configure GDO1 */
gpio_init(dev->params.gdo1, GPIO_DIR_IN, GPIO_NOPULL);
/* Configure SPI */
spi_acquire(dev->params.spi);
spi_init_master(dev->params.spi, SPI_CONF_FIRST_RISING, SPI_SPEED_5MHZ);
spi_release(dev->params.spi);
#ifndef CC110X_DONT_RESET
/* reset device*/
_power_up_reset(dev);
#endif
/* set default state */
dev->radio_state = RADIO_IDLE;
/* Write configuration to configuration registers */
cc110x_writeburst_reg(dev, 0x00, cc110x_default_conf, cc110x_default_conf_size);
/* Write PATABLE (power settings) */
cc110x_writeburst_reg(dev, CC110X_PATABLE, CC110X_DEFAULT_PATABLE, 8);
/* set base frequency */
cc110x_set_base_freq_raw(dev, CC110X_DEFAULT_FREQ);
/* Set default channel number */
cc110x_set_channel(dev, CC110X_DEFAULT_CHANNEL);
/* set default node id */
#ifdef CPUID_ID_LEN
if (CPUID_ID_LEN>0) {
char cpuid[CPUID_ID_LEN];
cpuid_get(cpuid);
cc110x_set_address(dev, (uint8_t) cpuid[CPUID_ID_LEN-1]);
}
#endif
LOG_INFO("cc110x: initialized with address=%u and channel=%i\n",
(unsigned)dev->radio_address,
dev->radio_channel);
return 0;
}
uint8_t cc110x_set_address(cc110x_t *dev, uint8_t address)
{
DEBUG("%s:%s:%u setting address %u\n", RIOT_FILE_RELATIVE, __func__,
__LINE__, (unsigned)address);
if (!(address < MIN_UID) || (address > MAX_UID)) {
if (dev->radio_state != RADIO_UNKNOWN) {
cc110x_write_register(dev, CC110X_ADDR, address);
dev->radio_address = address;
return address;
}
}
return 0;
}
void cc110x_set_base_freq_raw(cc110x_t *dev, const char* freq_array)
{
#if ENABLE_DEBUG == 1
uint8_t _tmp[] = { freq_array[2], freq_array[1], freq_array[0], 0x00};
uint32_t *FREQ = (uint32_t*) _tmp;
DEBUG("cc110x_set_base_freq_raw(): setting base frequency to %uHz\n",
(26000000>>16) * (unsigned)(*FREQ));
#endif
cc110x_writeburst_reg(dev, CC110X_FREQ2, freq_array, 3);
}
void cc110x_set_monitor(cc110x_t *dev, uint8_t mode)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
cc110x_write_register(dev, CC110X_PKTCTRL1, mode ? 0x04 : 0x06);
}
void cc110x_setup_rx_mode(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
/* Stay in RX mode until end of packet */
cc110x_write_reg(dev, CC110X_MCSM2, 0x07);
cc110x_switch_to_rx(dev);
}
void cc110x_switch_to_rx(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_rx();
#endif
gpio_irq_disable(dev->params.gdo2);
/* flush RX fifo */
cc110x_strobe(dev, CC110X_SIDLE);
cc110x_strobe(dev, CC110X_SFRX);
dev->radio_state = RADIO_RX;
cc110x_write_reg(dev, CC110X_IOCFG2, 0x6);
cc110x_strobe(dev, CC110X_SRX);
gpio_irq_enable(dev->params.gdo2);
}
void cc110x_wakeup_from_rx(cc110x_t *dev)
{
if (dev->radio_state != RADIO_RX) {
return;
}
LOG_DEBUG("cc110x: switching to idle mode\n");
cc110x_strobe(dev, CC110X_SIDLE);
dev->radio_state = RADIO_IDLE;
}
void cc110x_switch_to_pwd(cc110x_t *dev)
{
LOG_DEBUG("cc110x: switching to powerdown mode\n");
cc110x_wakeup_from_rx(dev);
cc110x_strobe(dev, CC110X_SPWD);
dev->radio_state = RADIO_PWD;
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_off();
#endif
}
#ifndef MODULE_CC110X_HOOKS
int16_t cc110x_set_channel(cc110x_t *dev, uint8_t channr)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
if (channr > MAX_CHANNR) {
return -1;
}
cc110x_write_register(dev, CC110X_CHANNR, channr * 10);
dev->radio_channel = channr;
return channr;
}
#endif
#ifndef CC110X_DONT_RESET
static void _reset(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
cc110x_wakeup_from_rx(dev);
cc110x_cs(dev);
cc110x_strobe(dev, CC110X_SRES);
xtimer_usleep(100);
}
static void _power_up_reset(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
gpio_set(dev->params.cs);
gpio_clear(dev->params.cs);
gpio_set(dev->params.cs);
xtimer_usleep(RESET_WAIT_TIME);
_reset(dev);
}
#endif
void cc110x_write_register(cc110x_t *dev, uint8_t r, uint8_t value)
{
/* Save old radio state */
uint8_t old_state = dev->radio_state;
/* Wake up from RX (no effect if in other mode) */
cc110x_wakeup_from_rx(dev);
cc110x_write_reg(dev, r, value);
/* Have to put radio back to RX if old radio state
* was RX, otherwise no action is necessary */
if (old_state == RADIO_RX) {
cc110x_switch_to_rx(dev);
}
}
int cc110x_rd_set_mode(cc110x_t *dev, int mode)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
int result;
/* Get current radio mode */
if ((dev->radio_state == RADIO_UNKNOWN) || (dev->radio_state == RADIO_PWD)) {
result = RADIO_MODE_OFF;
}
else {
result = RADIO_MODE_ON;
}
switch(mode) {
case RADIO_MODE_ON:
LOG_DEBUG("cc110x: switching to RX mode\n");
cc110x_setup_rx_mode(dev); /* Set chip to desired mode */
break;
case RADIO_MODE_OFF:
gpio_irq_disable(dev->params.gdo2); /* Disable interrupts */
cc110x_switch_to_pwd(dev); /* Set chip to power down mode */
break;
case RADIO_MODE_GET:
/* do nothing, just return current mode */
default:
/* do nothing */
break;
}
/* Return previous mode */
return result;
}

1
drivers/cc110x/gnrc_cc110x/Makefile

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

192
drivers/cc110x/gnrc_cc110x/gnrc_netdev2_cc110x.c

@ -0,0 +1,192 @@
/*
* 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.
*/
#include <assert.h>
#include <sys/uio.h>
#include "net/netdev2.h"
#include "net/gnrc.h"
#include "cc110x.h"
#include "cc110x/cc110x-netdev2.h"
#include "net/gnrc/gnrc_netdev2.h"
#include "od.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int _send(gnrc_netdev2_t *gnrc_netdev2, gnrc_pktsnip_t *pkt)
{
cc110x_pkt_t cc110x_pkt;
netdev2_t *dev = gnrc_netdev2->dev;
netdev2_cc110x_t *netdev_cc110x = (netdev2_cc110x_t *) dev;
cc110x_t *cc110x = &netdev_cc110x->cc110x;
assert(pkt != NULL);
assert(dev->driver == &netdev2_cc110x_driver);
gnrc_netif_hdr_t *netif_hdr;
gnrc_pktsnip_t *payload;
payload = pkt->next;
if (pkt->type != GNRC_NETTYPE_NETIF) {
DEBUG("gnrc_netdev2_cc110x: First header was not generic netif header\n");
gnrc_pktbuf_release(pkt);
return -EBADMSG;
}
netif_hdr = (gnrc_netif_hdr_t *) pkt->data;
/* set up header */
if (netif_hdr->src_l2addr_len == 1) {
uint8_t *_src_addr = gnrc_netif_hdr_get_src_addr(netif_hdr);
cc110x_pkt.phy_src = *_src_addr;
}
else {
cc110x_pkt.phy_src = cc110x->radio_address;
}
if (netif_hdr->flags & (GNRC_NETIF_HDR_FLAGS_BROADCAST |
GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
cc110x_pkt.address = 0;
}
else {
uint8_t *_dst_addr = gnrc_netif_hdr_get_dst_addr(netif_hdr);
cc110x_pkt.address = _dst_addr[netif_hdr->dst_l2addr_len-1];
}
switch (payload->type) {
#ifdef MODULE_GNRC_SIXLOWPAN
case GNRC_NETTYPE_SIXLOWPAN:
cc110x_pkt.flags = 1;
break;
#endif
default:
cc110x_pkt.flags = 0;
}
struct iovec vector;
vector.iov_base = (char*)&cc110x_pkt;
vector.iov_len = sizeof(cc110x_pkt_t);
unsigned payload_len = 0;
uint8_t *pos = cc110x_pkt.data;
while (payload) {
payload_len += payload->size;
if (payload_len > CC110X_MAX_DATA_LENGTH) {
DEBUG("gnrc_netdev2_cc110x: payload length exceeds maximum"
"(%u>%u)\n", payload_len, CC110X_MAX_DATA_LENGTH);
gnrc_pktbuf_release(pkt);
return -EBADMSG;
}
memcpy(pos, payload->data, payload->size);
pos += payload->size;
payload = payload->next;
}
/* pkt has been copied into iovec, we're done with it. */
gnrc_pktbuf_release(pkt);
cc110x_pkt.length = (uint8_t) payload_len + CC110X_HEADER_LENGTH;
DEBUG("gnrc_netdev2_cc110x: sending packet from %u to %u with payload "
"length %u\n",
(unsigned)cc110x_pkt.phy_src,
(unsigned)cc110x_pkt.address,
(unsigned)cc110x_pkt.length);
return dev->driver->send(dev, &vector, 1);
}
static gnrc_pktsnip_t *_recv(gnrc_netdev2_t *gnrc_netdev2)
{
netdev2_t *dev = gnrc_netdev2->dev;
cc110x_t *cc110x = &((netdev2_cc110x_t*) dev)->cc110x;
cc110x_pkt_t *cc110x_pkt = &cc110x->pkt_buf.packet;
int payload_length = cc110x_pkt->length - CC110X_HEADER_LENGTH;
int nettype;
int addr_len;
switch (cc110x_pkt->flags) {
#ifdef MODULE_GNRC_SIXLOWPAN
case 1:
addr_len = 8;
nettype = GNRC_NETTYPE_SIXLOWPAN;
break;
#endif
default:
addr_len = 1;
nettype = GNRC_NETTYPE_UNDEF;
}
/* copy packet payload into pktbuf */
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, cc110x_pkt->data,
payload_length, nettype);
if(!pkt) {
DEBUG("cc110x: _recv: cannot allocate pktsnip.\n");
return NULL;
}
gnrc_pktsnip_t *netif_hdr;
netif_hdr = gnrc_pktbuf_add(NULL, NULL,
sizeof(gnrc_netif_hdr_t) + 2*addr_len,
GNRC_NETTYPE_NETIF);
if (netif_hdr == NULL) {
DEBUG("gnrc_netdev2_cc110x: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt);
return NULL;
}
gnrc_netif_hdr_init(netif_hdr->data, addr_len, addr_len);
if (addr_len == 8) {
uint64_t src_addr = cc110x_pkt->phy_src;
uint64_t dst_addr = cc110x_pkt->address;
gnrc_netif_hdr_set_src_addr(netif_hdr->data, (uint8_t*)&src_addr, addr_len);
gnrc_netif_hdr_set_dst_addr(netif_hdr->data, (uint8_t*)&dst_addr, addr_len);
}
else {
gnrc_netif_hdr_set_src_addr(netif_hdr->data, (uint8_t*)&cc110x_pkt->phy_src, addr_len);
gnrc_netif_hdr_set_dst_addr(netif_hdr->data, (uint8_t*)&cc110x_pkt->address, addr_len);
}
((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid = thread_getpid();
((gnrc_netif_hdr_t *)netif_hdr->data)->lqi = cc110x->pkt_buf.lqi;
((gnrc_netif_hdr_t *)netif_hdr->data)->rssi = cc110x->pkt_buf.rssi;
DEBUG("gnrc_netdev2_cc110x: received packet from %02x"
" of length %u\n",
(unsigned)cc110x_pkt->phy_src,
(unsigned)cc110x_pkt->length-CC110X_HEADER_LENGTH);
#if defined(MODULE_OD) && ENABLE_DEBUG
od_hex_dump(cc110x_pkt->data, payload_length, OD_WIDTH_DEFAULT);
#endif
pkt->next = netif_hdr;
return pkt;
}
int gnrc_netdev2_cc110x_init(gnrc_netdev2_t *gnrc_netdev2, netdev2_t *dev)
{
gnrc_netdev2->send = _send;
gnrc_netdev2->recv = _recv;
gnrc_netdev2->dev = dev;
return 0;
}

132
drivers/include/cc110x.h

@ -0,0 +1,132 @@
/*
* Copyright (C) 2014 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.
*/
/**
* @defgroup drivers_cc110x CC110x
* @ingroup drivers
* @brief TI CC110x
* @{
* @file
* @brief Public interface for cc110x driver
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef CC110X_H
#define CC110X_H
#ifdef __cplusplus
extern "C" {
#endif
#include "periph/spi.h"
#include "periph/gpio.h"
#include "cc110x/cc110x-internal.h"
/**
* @brief Struct for holding cc110x IO parameters
*/
typedef struct cc110x_params {
spi_t spi; /**< what */
gpio_t cs; /**< does */
gpio_t gdo0; /**< this */
gpio_t gdo1; /**< look */
gpio_t gdo2; /**< like */
} cc110x_params_t;
/**
* @brief forward declaration
*/
typedef struct cc110x cc110x_t;
/**
* @brief Struct for holding cc110x device state
*/
struct cc110x {
cc110x_params_t params; /**< cc110x IO configuration */
cc110x_statistic_t cc110x_statistic; /**< Statistic values for
debugging */
uint8_t radio_state; /**< Radio state */
uint8_t radio_channel; /**< current Radio channel */
uint8_t radio_address; /**< current Radio address */
cc110x_pkt_buf_t pkt_buf; /**< RX/TX buffer */
void (*isr_cb)(cc110x_t *dev, void* arg); /**< isr callback */
void *isr_cb_arg; /**< isr callback argument */
};
/**
* @brief Setup cc110x device parameters
*
* @param[in] dev device struct to set up
* @param[in] params struct holding parameters
*
* @return always succeeds
*/
int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params);
/**
* @brief Set cc110x channel number
*
* @param[in] dev device to work on
* @param[in] channr guess what
*
* @return nr of set channel on success
* @return -1 on error
*/
int16_t cc110x_set_channel(cc110x_t *dev, uint8_t channr);
/**
* @brief Send raw cc110x packet
*
* @param[in] dev Device to send on
* @param[in] packet ptr to packet to be sent
*
* @return size of packet on success
* @return <0 on error
*/
int cc110x_send(cc110x_t *dev, cc110x_pkt_t *packet);
/**
* @brief Set cc110x radio address
*
* @param[in] dev device to query
*
* @return nr of currently set address
*/
uint8_t cc110x_get_address(cc110x_t *dev);
/**
* @brief Set cc110x radio address
*
* @param[in] dev device to work on
* @param[in] address new address
*
* @return address set on success
* @return 0 on error
*/
uint8_t cc110x_set_address(cc110x_t *dev, uint8_t address);
/**
* @brief Set cc110x monitor mode setting
*
* @param[in] dev device to work on
* @param[in] mode mode to set (0 or 1)
*/
void cc110x_set_monitor(cc110x_t *dev, uint8_t mode);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_H */
/** @} */

46
drivers/include/cc110x/cc110x-defaultsettings.h

@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser