580 lines
16 KiB
C
580 lines
16 KiB
C
/*
|
|
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* This file is part of the Contiki operating system.
|
|
*
|
|
* $Id: sicslowmac.c,v 1.5 2008/11/08 03:29:15 c_oflynn Exp $
|
|
*/
|
|
|
|
|
|
/**
|
|
* \file
|
|
* Example glue code between the existing MAC code and the
|
|
* Contiki mac interface
|
|
*
|
|
* \author
|
|
* Adam Dunkels <adam@sics.se>
|
|
* Eric Gnoske <egnoske@gmail.com>
|
|
* Blake Leverett <bleverett@gmail.com>
|
|
*
|
|
* \addtogroup rf230mac
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <avr/eeprom.h>
|
|
#include <util/delay.h>
|
|
#include "net/rime/rimebuf.h"
|
|
#include "zmac.h"
|
|
#include "mac.h"
|
|
#include "frame.h"
|
|
#include "radio.h"
|
|
#include "tcpip.h"
|
|
#include "sicslowmac.h"
|
|
#include "sicslowpan.h"
|
|
#include "ieee-15-4-manager.h"
|
|
|
|
/* Macros */
|
|
#define DEBUG 0
|
|
#define MAX_EVENTS 10
|
|
|
|
#if DEBUG
|
|
#define PRINTF(...) printf(__VA_ARGS__)
|
|
#define SICSLOW_CORRECTION_DELAY 70
|
|
#else
|
|
#define PRINTF(...)
|
|
#define SICSLOW_CORRECTION_DELAY 7
|
|
#endif
|
|
|
|
#ifdef JACKDAW
|
|
#include "sicslow_ethernet.h"
|
|
#define LOG_FRAME(x,y) mac_logTXtoEthernet(x,y)
|
|
#else
|
|
#define LOG_FRAME(x,y)
|
|
#endif
|
|
|
|
/* Globals */
|
|
static struct mac_driver mac_driver_struct;
|
|
static struct mac_driver *pmac_driver = &mac_driver_struct;
|
|
extern ieee_15_4_manager_t ieee15_4ManagerAddress;
|
|
static parsed_frame_t *parsed_frame;
|
|
|
|
|
|
const struct mac_driver sicslowmac_driver = {
|
|
sicslowmac_dataRequest,
|
|
/* read_packet, */
|
|
/* set_receive_function, */
|
|
/* on, */
|
|
/* off, */
|
|
};
|
|
|
|
static struct {
|
|
uint8_t head;
|
|
uint8_t tail;
|
|
event_object_t event_object[MAX_EVENTS];
|
|
} event_queue;
|
|
|
|
/* Prototypes */
|
|
static void setinput(void (*r)(const struct mac_driver *d));
|
|
void (*pinput)(const struct mac_driver *r);
|
|
void sicslowmac_unknownIndication(void);
|
|
|
|
|
|
void (*sicslowmac_snifferhook)(const struct mac_driver *r) = NULL;
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief Checks for any pending events in the queue.
|
|
*
|
|
* \return True if there is a pending event, else false.
|
|
*/
|
|
uint8_t
|
|
mac_event_pending(void)
|
|
{
|
|
return (event_queue.head != event_queue.tail);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief Puts an event into the queue of events.
|
|
*
|
|
* \param object is a pointer to the event to add to queue.
|
|
*/
|
|
void
|
|
mac_put_event(event_object_t *object)
|
|
{
|
|
uint8_t newhead;
|
|
|
|
if ((event_queue.head + 1) % MAX_EVENTS == event_queue.tail){
|
|
/* queue full, get outta here */
|
|
return;
|
|
}
|
|
|
|
newhead = event_queue.head;
|
|
|
|
/* store in queue */
|
|
event_queue.event_object[newhead] = *object;
|
|
|
|
/* calculate new head index */
|
|
newhead++;
|
|
if (newhead >= MAX_EVENTS){
|
|
newhead = 0;
|
|
}
|
|
event_queue.head = newhead;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief Pulls an event from the event queue.
|
|
* Assumes that there is an event in the queue. See mac_event_pending().
|
|
*
|
|
* \return Pointer to the event object, or NULL in the event of empty queue.
|
|
*/
|
|
event_object_t
|
|
*mac_get_event(void)
|
|
{
|
|
event_object_t *object = NULL;
|
|
volatile uint8_t newtail;
|
|
|
|
newtail = event_queue.tail;
|
|
|
|
object = &(event_queue.event_object[newtail]);
|
|
|
|
/* calculate new tail */
|
|
newtail++;
|
|
if (newtail >= MAX_EVENTS){
|
|
newtail = 0;
|
|
}
|
|
|
|
event_queue.tail = newtail;
|
|
|
|
return(object);
|
|
}
|
|
|
|
void mac_pollhandler(void)
|
|
{
|
|
mac_task(0, NULL);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief This is the main loop task for the MAC. Called by the
|
|
* main application loop.
|
|
*/
|
|
void
|
|
mac_task(process_event_t ev, process_data_t data)
|
|
{
|
|
/* check for event in queue */
|
|
event_object_t *event;
|
|
|
|
if(mac_event_pending()){
|
|
|
|
event = mac_get_event();
|
|
|
|
/* Handle events from radio */
|
|
if (event){
|
|
|
|
if (event->event == MAC_EVENT_RX){
|
|
/* got a frame, find out with kind of frame */
|
|
parsed_frame = (parsed_frame_t *)event->data;
|
|
if (parsed_frame->fcf->frameType == DATAFRAME){
|
|
sicslowmac_dataIndication();
|
|
} else {
|
|
|
|
/* Hook to cath unknown frames */
|
|
sicslowmac_unknownIndication();
|
|
}
|
|
|
|
|
|
/* Frame no longer in use */
|
|
parsed_frame->in_use = false;
|
|
}
|
|
|
|
if (event->event == MAC_EVENT_DROPPED){
|
|
/* Frame was dropped */
|
|
PRINTF("sicslowmac: Frame Dropped!\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
setinput(void (*r)(const struct mac_driver *d))
|
|
{
|
|
pinput = r;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static uint8_t dest_reversed[8];
|
|
static uint8_t src_reversed[8];
|
|
|
|
void
|
|
sicslowmac_dataIndication(void)
|
|
{
|
|
rimebuf_clear();
|
|
|
|
/* Finally, get the stuff into the rime buffer.... */
|
|
rimebuf_copyfrom(parsed_frame->payload, parsed_frame->payload_length);
|
|
rimebuf_set_datalen(parsed_frame->payload_length);
|
|
|
|
memcpy(dest_reversed, (uint8_t *)parsed_frame->dest_addr, 8);
|
|
memcpy(src_reversed, (uint8_t *)parsed_frame->src_addr, 8);
|
|
|
|
/* Change addresses to expected byte order */
|
|
byte_reverse((uint8_t *)dest_reversed, 8);
|
|
byte_reverse((uint8_t *)src_reversed, 8);
|
|
|
|
rimebuf_set_addr(RIMEBUF_ADDR_RECEIVER, (const rimeaddr_t *)dest_reversed);
|
|
rimebuf_set_addr(RIMEBUF_ADDR_SENDER, (const rimeaddr_t *)src_reversed);
|
|
|
|
PRINTF("sicslowmac: hand off frame to sicslowpan \n");
|
|
pinput(pmac_driver);
|
|
}
|
|
|
|
void
|
|
sicslowmac_unknownIndication(void)
|
|
{
|
|
if (sicslowmac_snifferhook) {
|
|
|
|
rimebuf_clear();
|
|
|
|
/* Finally, get the stuff into the rime buffer.... */
|
|
rimebuf_copyfrom(parsed_frame->payload, parsed_frame->payload_length);
|
|
rimebuf_set_datalen(parsed_frame->payload_length);
|
|
|
|
memcpy(dest_reversed, (uint8_t *)parsed_frame->dest_addr, 8);
|
|
memcpy(src_reversed, (uint8_t *)parsed_frame->src_addr, 8);
|
|
|
|
/* Change addresses to expected byte order */
|
|
byte_reverse((uint8_t *)dest_reversed, 8);
|
|
byte_reverse((uint8_t *)src_reversed, 8);
|
|
|
|
rimebuf_set_addr(RIMEBUF_ADDR_RECEIVER, (const rimeaddr_t *)dest_reversed);
|
|
rimebuf_set_addr(RIMEBUF_ADDR_SENDER, (const rimeaddr_t *)src_reversed);
|
|
|
|
PRINTF("sicslowmac: hand off frame to sniffer \n");
|
|
|
|
sicslowmac_snifferhook(pmac_driver);
|
|
}
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief This is the implementation of the 15.4 MAC Data Request
|
|
* primitive.
|
|
*
|
|
* \return Integer denoting success or failure.
|
|
* \retval 0 Failure.
|
|
* \retval 1 Success.
|
|
*
|
|
* The data request primitive creates the frame header based
|
|
* on static and dynamic data. The static data will be refined
|
|
* in phase II of the project. The frame payload and length are
|
|
* retrieved from the rime buffer and rime length respectively.
|
|
*
|
|
* When the header and payload are assembled into the
|
|
* frame_create_params structure, the frame is created
|
|
* by a call to frame_tx_create and then transmited via
|
|
* radio_send_data.
|
|
*/
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
sicslowmac_dataRequest(void)
|
|
{
|
|
|
|
_delay_ms(SICSLOW_CORRECTION_DELAY);
|
|
|
|
/* create structure to store result. */
|
|
frame_create_params_t params;
|
|
frame_result_t result;
|
|
|
|
/* Save the msduHandle in a global variable. */
|
|
msduHandle = rimebuf_attr(RIMEBUF_ATTR_PACKET_ID);
|
|
|
|
/* Build the FCF. */
|
|
params.fcf.frameType = DATAFRAME;
|
|
params.fcf.securityEnabled = false;
|
|
params.fcf.framePending = false;
|
|
params.fcf.ackRequired = rimebuf_attr(RIMEBUF_ATTR_RELIABLE);
|
|
params.fcf.panIdCompression = false;
|
|
|
|
/* Insert IEEE 802.15.4 (2003) version bit. */
|
|
params.fcf.frameVersion = IEEE802154_2003;
|
|
|
|
/* Increment and set the data sequence number. */
|
|
params.seq = macDSN++;
|
|
|
|
/* Complete the addressing fields. */
|
|
/**
|
|
\todo For phase 1 the addresses are all long. We'll need a mechanism
|
|
in the rime attributes to tell the mac to use long or short for phase 2.
|
|
*/
|
|
params.fcf.srcAddrMode = LONGADDRMODE;
|
|
params.dest_pid = ieee15_4ManagerAddress.get_dst_panid();
|
|
|
|
/*
|
|
* If the output address is NULL in the Rime buf, then it is broadcast
|
|
* on the 802.15.4 network.
|
|
*/
|
|
if(rimeaddr_cmp(rimebuf_addr(RIMEBUF_ADDR_RECEIVER), &rimeaddr_null) ) {
|
|
/* Broadcast requires short address mode. */
|
|
params.fcf.destAddrMode = SHORTADDRMODE;
|
|
params.dest_pid = BROADCASTPANDID;
|
|
params.dest_addr.addr16 = BROADCASTADDR;
|
|
|
|
} else {
|
|
|
|
/* Phase 1.5 - end nodes send to anyone? */
|
|
memcpy(¶ms.dest_addr, (uint8_t *)rimebuf_addr(RIMEBUF_ADDR_RECEIVER), LONG_ADDR_LEN);
|
|
|
|
/* Change from sicslowpan byte arrangement to sicslowmac */
|
|
byte_reverse((uint8_t*)¶ms.dest_addr.addr64, LONG_ADDR_LEN);
|
|
|
|
/* Phase 1 - end nodes only sends to pan coordinator node. */
|
|
/* params.dest_addr.addr64 = ieee15_4ManagerAddress.get_coord_long_addr(); */
|
|
params.fcf.destAddrMode = LONGADDRMODE;
|
|
}
|
|
|
|
/* Set the source PAN ID to the global variable. */
|
|
params.src_pid = ieee15_4ManagerAddress.get_src_panid();
|
|
|
|
/*
|
|
* Set up the source address using only the long address mode for
|
|
* phase 1.
|
|
*/
|
|
params.src_addr.addr64 = ieee15_4ManagerAddress.get_long_addr();
|
|
|
|
/* Copy the payload data. */
|
|
params.payload_len = rimebuf_datalen();
|
|
params.payload = rimebuf_dataptr();
|
|
|
|
/* Create transmission frame. */
|
|
frame_tx_create(¶ms, &result);
|
|
|
|
/* Log if needed */
|
|
LOG_FRAME(¶ms, &result);
|
|
|
|
/* Retry up to this many times to send the packet if radio is busy */
|
|
uint8_t retry_count = 3;
|
|
|
|
while(retry_count) {
|
|
|
|
PRINTF("sicslowmac: sending packet of length %d to radio, result:", result.length);
|
|
|
|
|
|
|
|
/* Send data to radio. */
|
|
radio_status_t rv = radio_send_data(result.length, result.frame);
|
|
|
|
if (rv == RADIO_SUCCESS) {
|
|
PRINTF(" Success\n");
|
|
|
|
return 1; /* True says that the packet could be sent */
|
|
}
|
|
|
|
|
|
if (rv != RADIO_WRONG_STATE) {
|
|
PRINTF(" Failed\n");
|
|
return 0;
|
|
}
|
|
|
|
PRINTF(" Radio busy, retrying\n");
|
|
|
|
/** \todo: Fix delay in sicslowmac so they do not block receiving */
|
|
|
|
//We have blocking delay here, it is safest this way. BUT doesn't solve the
|
|
//problem of TX when you are RXing.. as the RX code can't execute!
|
|
if (retry_count == 3) {
|
|
_delay_ms(10);
|
|
} else if (retry_count == 2) {
|
|
_delay_ms(50);
|
|
} else if (retry_count == 1) {
|
|
_delay_ms(200);
|
|
}
|
|
|
|
retry_count--;
|
|
}
|
|
|
|
PRINTF("sicslowmac: Unable to send packet, dropped\n");
|
|
return 0;
|
|
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief Stub function that will be implemented in phase 2 to cause
|
|
* end nodes to sleep.
|
|
*/
|
|
int
|
|
mac_wake(void)
|
|
{
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief Stub function that will be implemented in phase 2 to cause
|
|
* end nodes to sleep.
|
|
*/
|
|
int
|
|
mac_sleep(void)
|
|
{
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
const struct mac_driver *
|
|
sicslowmac_init(const struct radio_driver *d)
|
|
{
|
|
/* AD: commented out the radio_driver code for now.*/
|
|
/* radio = d;
|
|
radio->set_receive_function(input_packet);
|
|
radio->on();*/
|
|
|
|
return &sicslowmac_driver;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief This is the implementation of the 15.4 MAC Reset Request
|
|
* primitive.
|
|
* \param setDefaultPIB True if the default PIB values should be set.
|
|
* \return Integer denoting success or failure.
|
|
* \retval 0 Failure.
|
|
* \retval 1 Success.
|
|
*
|
|
* Sets all PIB values to default.
|
|
*/
|
|
void
|
|
sicslowmac_resetRequest (bool setDefaultPIB)
|
|
{
|
|
if(setDefaultPIB){
|
|
/* initialize all of the MAC PIB variables to their default values */
|
|
macCoordShortAddress = 0xffff;
|
|
macDSN = rand() % 256;
|
|
macSrcPANId = SOURCE_PAN_ID;
|
|
macDstPANId = DEST_PAN_ID;
|
|
macShortAddress = 0xffff;
|
|
/* Setup the address of this device by reading a stored address from eeprom. */
|
|
/** \todo This might be read from the serial eeprom onboard Raven. */
|
|
AVR_ENTER_CRITICAL_REGION();
|
|
eeprom_read_block ((void *)&macLongAddr, EEPROMMACADDRESS, 8);
|
|
|
|
byte_reverse((uint8_t *) &macLongAddr, 8);
|
|
|
|
|
|
AVR_LEAVE_CRITICAL_REGION();
|
|
}
|
|
}
|
|
|
|
parsed_frame_t * sicslowmac_get_frame(void)
|
|
{
|
|
return parsed_frame;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
struct mac_driver * sicslowmac_get_driver(void)
|
|
{
|
|
return pmac_driver;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
PROCESS(mac_process, "802.15.4 MAC process");
|
|
PROCESS_THREAD(mac_process, ev, data)
|
|
{
|
|
|
|
PROCESS_POLLHANDLER(mac_pollhandler());
|
|
|
|
|
|
PROCESS_BEGIN();
|
|
|
|
radio_status_t return_value;
|
|
|
|
/* init radio */
|
|
/** \todo: this screws up if calosc is set to TRUE, find out why? */
|
|
return_value = radio_init(false, NULL, NULL, NULL);
|
|
|
|
#if DEBUG
|
|
if (return_value == RADIO_SUCCESS) {
|
|
printf("Radio init successful.\n");
|
|
} else {
|
|
printf("Radio init failed with return: %d\n", return_value);
|
|
}
|
|
#endif
|
|
|
|
uint8_t eeprom_channel;
|
|
uint8_t eeprom_check;
|
|
|
|
eeprom_channel = eeprom_read_byte(9);
|
|
eeprom_check = eeprom_read_byte(10);
|
|
|
|
if ((eeprom_channel < 11) || (eeprom_channel > 26) || ((uint8_t)eeprom_channel != (uint8_t)~eeprom_check)) {
|
|
eeprom_channel = 24; //Default
|
|
}
|
|
|
|
radio_set_operating_channel(eeprom_channel);
|
|
radio_use_auto_tx_crc(true);
|
|
radio_set_trx_state(TRX_OFF);
|
|
|
|
mac_init();
|
|
|
|
/* Set up MAC function pointers and sicslowpan callback. */
|
|
pmac_driver->set_receive_function = setinput;
|
|
pmac_driver->send = sicslowmac_dataRequest;
|
|
sicslowpan_init(pmac_driver);
|
|
|
|
ieee_15_4_init(&ieee15_4ManagerAddress);
|
|
|
|
radio_set_trx_state(RX_AACK_ON);
|
|
|
|
while(1) {
|
|
PROCESS_YIELD();
|
|
mac_task(ev, data);
|
|
|
|
}
|
|
|
|
PROCESS_END();
|
|
}
|
|
|
|
void byte_reverse(uint8_t * bytes, uint8_t num)
|
|
{
|
|
uint8_t tempbyte;
|
|
|
|
uint8_t i, j;
|
|
|
|
i = 0;
|
|
j = num - 1;
|
|
|
|
while(i < j) {
|
|
tempbyte = bytes[i];
|
|
bytes[i] = bytes[j];
|
|
bytes[j] = tempbyte;
|
|
|
|
j--;
|
|
i++;
|
|
}
|
|
|
|
return;
|
|
}
|