
19 changed files with 2233 additions and 0 deletions
@ -0,0 +1,3 @@
|
||||
DIRS += gnrc_cc110x
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -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); |
@ -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); |
||||
} |
@ -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; |
||||
} |
@ -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; |
||||
} |
@ -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; |
||||
} |
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base |
@ -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; |
||||
} |
@ -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 */ |
||||
/** @} */ |