/* * Copyright (c) 2014, NXP and SICS Swedish ICT. * 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. * */ /** * \file * Contiki driver for NXP JN516X using MMAC interface * \authors * Beshr Al Nahas * Simon Duquennot * Atis Elsts * */ #include #include "contiki.h" #include "dev/leds.h" #include "sys/rtimer.h" #include "net/packetbuf.h" #include "net/rime/rimestats.h" #include "net/netstack.h" #include "net/mac/frame802154.h" #include "lib/crc16.h" #include "lib/ringbufindex.h" #include "AppHardwareApi.h" #include "MMAC.h" #include "micromac-radio.h" #include "JPT.h" #include "PeripheralRegs.h" void vMMAC_SetChannelAndPower(uint8 u8Channel, int8 i8power); /* This driver configures the radio in PHY mode and does address decoding * and acknowledging in software. */ #define DEBUG DEBUG_NONE #include "net/ip/uip-debug.h" /* Perform CRC check for received packets in SW, * since we use PHY mode which does not calculate CRC in HW */ #define CRC_SW 1 #define CHECKSUM_LEN 2 /* Max packet duration: 5 + 127 + 2 bytes, 32us per byte */ #define MAX_PACKET_DURATION US_TO_RTIMERTICKS((127 + 2) * 32 + RADIO_DELAY_BEFORE_TX) /* Max ACK duration: 5 + 3 + 2 bytes */ #define MAX_ACK_DURATION US_TO_RTIMERTICKS((3 + 2) * 32 + RADIO_DELAY_BEFORE_TX) /* Test-mode pins output on dev-kit */ #define RADIO_TEST_MODE_HIGH_PWR 1 #define RADIO_TEST_MODE_ADVANCED 2 #define RADIO_TEST_MODE_DISABLED 0 #ifndef RADIO_TEST_MODE #define RADIO_TEST_MODE RADIO_TEST_MODE_DISABLED #endif /* RADIO_TEST_MODE */ /* The number of input buffers */ #ifndef MIRCOMAC_CONF_BUF_NUM #define MIRCOMAC_CONF_BUF_NUM 2 #endif /* MIRCOMAC_CONF_BUF_NUM */ /* Init radio channel */ #ifndef MICROMAC_CONF_CHANNEL #define MICROMAC_CONF_CHANNEL 26 #endif /* Default energy level threshold for clear channel detection */ #ifndef MICROMAC_CONF_CCA_THR #define MICROMAC_CONF_CCA_THR 39 /* approximately -85 dBm */ #endif /* MICROMAC_CONF_CCA_THR */ #if (JENNIC_CHIP == JN5169) #define OUTPUT_POWER_MAX 10 #define OUTPUT_POWER_MIN (-32) #define ABS_OUTPUT_POWER_MIN (32) #else #define OUTPUT_POWER_MAX 0 #define OUTPUT_POWER_MIN (-32) #endif /* Default Tx power [dBm] (between OUTPUT_POWER_MIN and OUTPUT_POWER_MAX) */ #ifndef MICROMAC_CONF_TX_POWER #define MICROMAC_CONF_TX_POWER 0 #endif /* Autoack */ #ifndef MICROMAC_CONF_AUTOACK #define MICROMAC_CONF_AUTOACK 1 #endif /* MICROMAC_CONF_AUTOACK */ #define RADIO_TO_RTIMER(X) ((rtimer_clock_t)((X) << (int32_t)8L)) /* Set radio always on for now because this is what Contiki MAC layers * expect. */ #ifndef MICROMAC_CONF_ALWAYS_ON #define MICROMAC_CONF_ALWAYS_ON 1 #endif /* MICROMAC_CONF_ALWAYS_ON */ #define BUSYWAIT_UNTIL(cond, max_time) \ do { \ rtimer_clock_t t0; \ t0 = RTIMER_NOW(); \ while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (max_time))) ; \ } while(0) /* Local variables */ static volatile signed char radio_last_rssi; static volatile uint8_t radio_last_correlation; /* LQI */ /* Did we miss a request to turn the radio on due to overflow? */ static volatile uint8_t missed_radio_on_request = 0; /* Poll mode disabled by default */ static uint8_t poll_mode = 0; /* (Software) frame filtering enabled by default */ static uint8_t frame_filtering = 1; /* (Software) autoack */ static uint8_t autoack_enabled = MICROMAC_CONF_AUTOACK; /* CCA before sending? Disabled by default. */ static uint8_t send_on_cca = 0; /* Current radio channel */ static int current_channel; /* Current set point tx power Actual tx power may be different. Use get_txpower() for actual power */ static int current_tx_power; /* an integer between 0 and 255, used only with cca() */ static uint8_t cca_thershold = MICROMAC_CONF_CCA_THR; /* Tx in progress? */ static volatile uint8_t tx_in_progress = 0; /* Are we currently listening? */ static volatile uint8_t listen_on = 0; /* Is the driver currently transmitting a software ACK? */ static uint8_t in_ack_transmission = 0; /* TX frame buffer */ static tsPhyFrame tx_frame_buffer; /* RX frame buffer */ static tsPhyFrame *rx_frame_buffer; /* Frame buffer pointer to read from */ static tsPhyFrame *input_frame_buffer = NULL; /* Ringbuffer for received packets in interrupt enabled mode */ static struct ringbufindex input_ringbuf; static tsPhyFrame input_array[MIRCOMAC_CONF_BUF_NUM]; /* SFD timestamp in RTIMER ticks */ static volatile uint32_t last_packet_timestamp = 0; /* Local functions prototypes */ static int on(void); static int off(void); static int is_packet_for_us(uint8_t *buf, int len, int do_send_ack); static void set_frame_filtering(uint8_t enable); static rtimer_clock_t get_packet_timestamp(void); static void set_txpower(int8_t power); void set_channel(int c); static void radio_interrupt_handler(uint32 mac_event); static int get_detected_energy(void); static int get_rssi(void); static void read_last_rssi(void); /*---------------------------------------------------------------------------*/ PROCESS(micromac_radio_process, "micromac_radio_driver"); /*---------------------------------------------------------------------------*/ /* Custom Radio parameters */ #ifndef RADIO_RX_MODE_POLL_MODE #define RADIO_PARAM_LAST_RSSI 0x80 #define RADIO_PARAM_LAST_PACKET_TIMESTAMP 0x81 #define RADIO_RX_MODE_POLL_MODE (1 << 2) #endif /* RADIO_RX_MODE_POLL_MODE */ #ifndef FRAME802154_IEEE802154E_2012 /* We define here the missing few features this driver needs from IEEE802.15.4e */ #define FRAME802154_IEEE802154E_2012 (0x02) /*----------------------------------------------------------------------------*/ uint16_t frame802154_get_pan_id() { return IEEE802154_PANID; } /*----------------------------------------------------------------------------*/ static void frame802154_has_panid(frame802154_fcf_t *fcf, int *has_src_pan_id, int *has_dest_pan_id) { int src_pan_id = 0; int dest_pan_id = 0; if(fcf == NULL) { return; } if(fcf->frame_version == FRAME802154_IEEE802154E_2012) { if(!fcf->panid_compression) { /* Compressed PAN ID == no PAN ID at all */ if(fcf->dest_addr_mode == fcf->dest_addr_mode) { /* No address or both addresses: include destination PAN ID */ dest_pan_id = 1; } else if(fcf->dest_addr_mode) { /* Only dest address, include dest PAN ID */ dest_pan_id = 1; } else if(fcf->src_addr_mode) { /* Only src address, include src PAN ID */ src_pan_id = 1; } } if(fcf->dest_addr_mode == 0 && fcf->dest_addr_mode == 1) { /* No address included, include dest PAN ID conditionally */ if(!fcf->panid_compression) { dest_pan_id = 1; /* Remove the following rule the day rows 2 and 3 from table 2a are fixed: */ } } if(fcf->dest_addr_mode == 0 && fcf->dest_addr_mode == 0) { /* Not meaningful, we include a PAN ID iff the compress flag is set, but * this is what the standard currently stipulates */ dest_pan_id = fcf->panid_compression; } } else /* No PAN ID in ACK */ if(fcf->frame_type != FRAME802154_ACKFRAME) { if(!fcf->panid_compression && fcf->src_addr_mode & 3) { /* If compressed, don't inclue source PAN ID */ src_pan_id = 1; } if(fcf->dest_addr_mode & 3) { dest_pan_id = 1; } } if(has_src_pan_id != NULL) { *has_src_pan_id = src_pan_id; } if(has_dest_pan_id != NULL) { *has_dest_pan_id = dest_pan_id; } } #endif /* FRAME802154_IEEE802154E_2012 */ /*---------------------------------------------------------------------------*/ static rtimer_clock_t get_packet_timestamp(void) { /* Save SFD timestamp, converted from radio timer to RTIMER */ last_packet_timestamp = RTIMER_NOW() - RADIO_TO_RTIMER((uint32_t)(u32MMAC_GetTime() - u32MMAC_GetRxTime())); return last_packet_timestamp; } /*---------------------------------------------------------------------------*/ static int init(void) { int put_index; tsExtAddr node_long_address; uint16_t node_short_address; tx_in_progress = 0; u32JPT_Init(); vMMAC_Enable(); /* Enable/disable interrupts */ if(poll_mode) { vMMAC_EnableInterrupts(NULL); vMMAC_ConfigureInterruptSources(0); } else { vMMAC_EnableInterrupts(&radio_interrupt_handler); } vMMAC_ConfigureRadio(); set_channel(MICROMAC_CONF_CHANNEL); set_txpower(MICROMAC_CONF_TX_POWER); vMMAC_GetMacAddress(&node_long_address); /* Short addresses are disabled by default */ node_short_address = (uint16_t)node_long_address.u32L; vMMAC_SetRxAddress(frame802154_get_pan_id(), node_short_address, &node_long_address); /* Disable hardware backoff */ vMMAC_SetTxParameters(1, 0, 0, 0); vMMAC_SetCutOffTimer(0, FALSE); /* Initialize ring buffer and first input packet pointer */ ringbufindex_init(&input_ringbuf, MIRCOMAC_CONF_BUF_NUM); /* get pointer to next input slot */ put_index = ringbufindex_peek_put(&input_ringbuf); if(put_index == -1) { rx_frame_buffer = NULL; printf("micromac_radio init:! no buffer available. Abort init.\n"); off(); return 0; } else { rx_frame_buffer = &input_array[put_index]; } input_frame_buffer = rx_frame_buffer; process_start(µmac_radio_process, NULL); #if RADIO_TEST_MODE == RADIO_TEST_MODE_HIGH_PWR /* Enable high power mode. * In this mode DIO2 goes high during RX * and DIO3 goes high during TX **/ vREG_SysWrite(REG_SYS_PWR_CTRL, u32REG_SysRead(REG_SYS_PWR_CTRL) | REG_SYSCTRL_PWRCTRL_RFRXEN_MASK | REG_SYSCTRL_PWRCTRL_RFTXEN_MASK); #elif RADIO_TEST_MODE == RADIO_TEST_MODE_ADVANCED /* output internal radio status on IO pins. * See Chris@NXP email */ vREG_SysWrite(REG_SYS_PWR_CTRL, u32REG_SysRead(REG_SYS_PWR_CTRL) | (1UL << 26UL)); #endif /* TEST_MODE */ return 1; } /*---------------------------------------------------------------------------*/ static int on(void) { /* No address matching or frame decoding */ if(rx_frame_buffer != NULL) { vMMAC_StartPhyReceive(rx_frame_buffer, (uint16_t)(E_MMAC_RX_START_NOW | E_MMAC_RX_NO_FCS_ERROR) /* means: reject FCS errors */ ); } else { missed_radio_on_request = 1; } ENERGEST_ON(ENERGEST_TYPE_LISTEN); listen_on = 1; return 1; } /*---------------------------------------------------------------------------*/ static int off(void) { listen_on = 0; tx_in_progress = 0; ENERGEST_OFF(ENERGEST_TYPE_LISTEN); /* The following would be needed with delayed Tx/Rx functions * vMMAC_SetCutOffTimer(0, FALSE);*/ vMMAC_RadioOff(); return 1; } /*---------------------------------------------------------------------------*/ static int transmit(unsigned short payload_len) { if(tx_in_progress) { return RADIO_TX_COLLISION; } tx_in_progress = 1; /* Energest */ if(listen_on) { ENERGEST_OFF(ENERGEST_TYPE_LISTEN); } ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); /* Transmit and wait */ vMMAC_StartPhyTransmit(&tx_frame_buffer, E_MMAC_TX_START_NOW | (send_on_cca ? E_MMAC_TX_USE_CCA : E_MMAC_TX_NO_CCA)); if(poll_mode) { BUSYWAIT_UNTIL(u32MMAC_PollInterruptSource(E_MMAC_INT_TX_COMPLETE), MAX_PACKET_DURATION); } else { if(in_ack_transmission) { /* as nested interupts are not possible, the tx flag will never be cleared */ BUSYWAIT_UNTIL(FALSE, MAX_ACK_DURATION); } else { /* wait until the tx flag is cleared */ BUSYWAIT_UNTIL(!tx_in_progress, MAX_PACKET_DURATION); } } /* Energest */ ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); if(listen_on) { ENERGEST_ON(ENERGEST_TYPE_LISTEN); } tx_in_progress = 0; /* Check error code */ int ret; uint32_t tx_error = u32MMAC_GetTxErrors(); if(tx_error == 0) { ret = RADIO_TX_OK; RIMESTATS_ADD(acktx); } else if(tx_error & E_MMAC_TXSTAT_ABORTED) { ret = RADIO_TX_ERR; RIMESTATS_ADD(sendingdrop); } else if(tx_error & E_MMAC_TXSTAT_CCA_BUSY) { ret = RADIO_TX_COLLISION; RIMESTATS_ADD(contentiondrop); } else if(tx_error & E_MMAC_TXSTAT_NO_ACK) { ret = RADIO_TX_NOACK; RIMESTATS_ADD(noacktx); } else { ret = RADIO_TX_ERR; } return ret; } /*---------------------------------------------------------------------------*/ static int prepare(const void *payload, unsigned short payload_len) { uint8_t i; uint16_t checksum; RIMESTATS_ADD(lltx); if(tx_in_progress) { return 1; } if(payload_len > 127 || payload == NULL) { return 1; } /* Copy payload to (soft) Ttx buffer */ memcpy(tx_frame_buffer.uPayload.au8Byte, payload, payload_len); i = payload_len; #if CRC_SW /* Compute CRC */ checksum = crc16_data(payload, payload_len, 0); tx_frame_buffer.uPayload.au8Byte[i++] = checksum; tx_frame_buffer.uPayload.au8Byte[i++] = (checksum >> 8) & 0xff; tx_frame_buffer.u8PayloadLength = payload_len + CHECKSUM_LEN; #else tx_frame_buffer.u8PayloadLength = payload_len; #endif return 0; } /*---------------------------------------------------------------------------*/ static int send(const void *payload, unsigned short payload_len) { if(prepare(payload, payload_len) == 0) { return transmit(payload_len); } else { return RADIO_TX_ERR; } } /*---------------------------------------------------------------------------*/ int get_channel(void) { return current_channel; } /*---------------------------------------------------------------------------*/ void set_channel(int c) { current_channel = c; /* will fine tune TX power as well */ vMMAC_SetChannel(current_channel); } /*---------------------------------------------------------------------------*/ static int is_broadcast_addr(uint8_t mode, uint8_t *addr) { int i = ((mode == FRAME802154_SHORTADDRMODE) ? 2 : 8); while(i-- > 0) { if(addr[i] != 0xff) { return 0; } } return 1; } /*---------------------------------------------------------------------------*/ /* Send an ACK */ static void send_ack(const frame802154_t *frame) { uint8_t buffer[3]; /* FCF: 2 octets */ buffer[0] = FRAME802154_ACKFRAME; buffer[1] = 0; /* Seqnum: 1 octets */ buffer[2] = frame->seq; in_ack_transmission = 1; send(&buffer, sizeof(buffer)); in_ack_transmission = 0; } /*---------------------------------------------------------------------------*/ /* Check if a packet is for us */ static int is_packet_for_us(uint8_t *buf, int len, int do_send_ack) { frame802154_t frame; int result; uint8_t parsed = frame802154_parse(buf, len, &frame); if(parsed) { if(frame.fcf.dest_addr_mode) { int has_dest_panid; frame802154_has_panid(&frame.fcf, NULL, &has_dest_panid); if(has_dest_panid && frame802154_get_pan_id() != FRAME802154_BROADCASTPANDID && frame.dest_pid != frame802154_get_pan_id() && frame.dest_pid != FRAME802154_BROADCASTPANDID) { /* Packet to another PAN */ return 0; } if(!is_broadcast_addr(frame.fcf.dest_addr_mode, frame.dest_addr)) { result = linkaddr_cmp((linkaddr_t *)frame.dest_addr, &linkaddr_node_addr); if(autoack_enabled && result && do_send_ack) { /* this is a unicast frame and sending ACKs is enabled */ send_ack(&frame); } return result; } } return 1; } else { return 0; } } /*---------------------------------------------------------------------------*/ static int read(void *buf, unsigned short bufsize) { int len = 0; uint16_t radio_last_rx_crc; uint8_t radio_last_rx_crc_ok = 1; len = input_frame_buffer->u8PayloadLength; if(len <= CHECKSUM_LEN) { input_frame_buffer->u8PayloadLength = 0; return 0; } else { len -= CHECKSUM_LEN; /* Check CRC */ #if CRC_SW uint16_t checksum = crc16_data(input_frame_buffer->uPayload.au8Byte, len, 0); radio_last_rx_crc = (uint16_t)(input_frame_buffer->uPayload.au8Byte[len + 1] << (uint16_t)8) | input_frame_buffer->uPayload.au8Byte[len]; radio_last_rx_crc_ok = (checksum == radio_last_rx_crc); if(!radio_last_rx_crc_ok) { RIMESTATS_ADD(badcrc); } #endif /* CRC_SW */ if(radio_last_rx_crc_ok) { /* If we are in poll mode we need to check the frame here */ if(poll_mode) { if(frame_filtering && !is_packet_for_us(input_frame_buffer->uPayload.au8Byte, len, 0)) { len = 0; } else { read_last_rssi(); } } if(len != 0) { bufsize = MIN(len, bufsize); memcpy(buf, input_frame_buffer->uPayload.au8Byte, bufsize); RIMESTATS_ADD(llrx); if(!poll_mode) { /* Not in poll mode: packetbuf should not be accessed in interrupt context */ packetbuf_set_attr(PACKETBUF_ATTR_RSSI, radio_last_rssi); packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, radio_last_correlation); } } } else { len = 0; } /* Disable further read attempts */ input_frame_buffer->u8PayloadLength = 0; } return len; } /*---------------------------------------------------------------------------*/ static void set_txpower(int8_t power) { if(power > OUTPUT_POWER_MAX) { current_tx_power = OUTPUT_POWER_MAX; } else { if(power < OUTPUT_POWER_MIN) { current_tx_power = OUTPUT_POWER_MIN; } else { current_tx_power = power; } } vMMAC_SetChannelAndPower(current_channel, current_tx_power); } /*--------------------------------------------------------------------------*/ static int get_txpower(void) { int actual_tx_power; #if (JENNIC_CHIP == JN5169) /* Actual tx power value rounded to nearest integer number */ const static int8 power_table [] = { -32, -30, -29, -29, /* -32 .. -29 */ -28, -28, -28, -28, /* -28 .. -25 */ -21, -21, -21, -2, /* -24 .. -21 */ -20, -19, -18, -17, /* -20 .. -17 */ -17, -17, -17, -10, /* -16 .. -13 */ -10, -10, -10, -9, /* -12 .. -09 */ -8, -7, -6, -6, /* -08 .. -05 */ -6, -6, 1, 1, /* -04 .. -01 */ 1, 1, 2, 3, /* 00 .. 03 */ 4, 5, 6, 7, /* 04 .. 07 */ 9, 9, 10 }; /* 08 .. 10 */ if(current_tx_power > OUTPUT_POWER_MAX) { actual_tx_power = OUTPUT_POWER_MAX; } else if(current_tx_power < OUTPUT_POWER_MIN) { actual_tx_power = OUTPUT_POWER_MIN; } else { actual_tx_power = power_table[current_tx_power + ABS_OUTPUT_POWER_MIN]; } #else /* Other JN516x chips */ if(current_tx_power < (-24)) { actual_tx_power = OUTPUT_POWER_MIN; } else if(current_tx_power < (-12)) { actual_tx_power = (-20); } else if(current_tx_power < 0) { actual_tx_power = (-9); } else { actual_tx_power = OUTPUT_POWER_MAX; } #endif return (int)actual_tx_power; } /*---------------------------------------------------------------------------*/ static int get_detected_energy(void) { const uint32 u32Samples = 8; return u8JPT_EnergyDetect(current_channel, u32Samples); } /*---------------------------------------------------------------------------*/ static int get_rssi(void) { /* this approximate formula for RSSI is taken from NXP internal docs */ return (7 * get_detected_energy() - 1970) / 20; } /*---------------------------------------------------------------------------*/ static void read_last_rssi(void) { uint8_t radio_last_rx_energy; radio_last_rx_energy = u8MMAC_GetRxLqi((uint8_t *)&radio_last_correlation); radio_last_rssi = i16JPT_ConvertEnergyTodBm(radio_last_rx_energy); } /*---------------------------------------------------------------------------*/ int receiving_packet(void) { return bMMAC_RxDetected(); } /*---------------------------------------------------------------------------*/ static int pending_packet(void) { if(!poll_mode) { return ringbufindex_peek_get(&input_ringbuf) != -1; } else { return u32MMAC_PollInterruptSource( E_MMAC_INT_RX_COMPLETE | E_MMAC_INT_RX_HEADER); } } /*---------------------------------------------------------------------------*/ static int cca(void) { bool_t is_channel_busy = bJPT_CCA(current_channel, E_JPT_CCA_MODE_CARRIER_OR_ENERGY, cca_thershold); return is_channel_busy == FALSE; } /*---------------------------------------------------------------------------*/ static void radio_interrupt_handler(uint32 mac_event) { uint32_t rx_status; uint8_t overflow = 0; int get_index; int put_index; int packet_for_me = 0; if(mac_event & E_MMAC_INT_TX_COMPLETE) { /* Transmission attempt has finished */ tx_in_progress = 0; } else if(mac_event & E_MMAC_INT_RX_COMPLETE) { rx_status = u32MMAC_GetRxErrors(); /* If rx is successful */ if(rx_status == 0) { /* Save SFD timestamp */ last_packet_timestamp = get_packet_timestamp(); if(!poll_mode && (mac_event & E_MMAC_INT_RX_COMPLETE)) { if(rx_frame_buffer->u8PayloadLength > CHECKSUM_LEN) { if(frame_filtering) { /* Check RX address */ packet_for_me = is_packet_for_us(rx_frame_buffer->uPayload.au8Byte, rx_frame_buffer->u8PayloadLength - CHECKSUM_LEN, 1); } else if(!frame_filtering) { packet_for_me = 1; } } if(!packet_for_me) { /* Prevent reading */ rx_frame_buffer->u8PayloadLength = 0; } else { /* read and cache RSSI and LQI values */ read_last_rssi(); /* Put received frame in queue */ ringbufindex_put(&input_ringbuf); if((get_index = ringbufindex_peek_get(&input_ringbuf)) != -1) { input_frame_buffer = &input_array[get_index]; } process_poll(µmac_radio_process); /* get pointer to next input slot */ put_index = ringbufindex_peek_put(&input_ringbuf); /* is there space? */ if(put_index != -1) { /* move rx_frame_buffer to next empty slot */ rx_frame_buffer = &input_array[put_index]; } else { overflow = 1; rx_frame_buffer = NULL; } } } } else { /* if rx is not successful */ if(rx_status & E_MMAC_RXSTAT_ABORTED) { RIMESTATS_ADD(badsynch); } else if(rx_status & E_MMAC_RXSTAT_ERROR) { RIMESTATS_ADD(badcrc); } else if(rx_status & E_MMAC_RXSTAT_MALFORMED) { RIMESTATS_ADD(toolong); } } } if(overflow) { off(); } else if(MICROMAC_CONF_ALWAYS_ON && (mac_event & (E_MMAC_INT_TX_COMPLETE | E_MMAC_INT_RX_COMPLETE))) { on(); } } /*---------------------------------------------------------------------------*/ PROCESS_THREAD(micromac_radio_process, ev, data) { PROCESS_BEGIN(); while(1) { PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); /* Pass received packets to upper layer */ int16_t read_index; /* Loop on accessing (without removing) a pending input packet */ while((read_index = ringbufindex_peek_get(&input_ringbuf)) != -1) { input_frame_buffer = &input_array[read_index]; /* Put packet into packetbuf for input callback */ packetbuf_clear(); int len = read(packetbuf_dataptr(), PACKETBUF_SIZE); /* is packet valid? */ if(len > 0) { packetbuf_set_datalen(len); NETSTACK_RDC.input(); } /* Remove packet from ringbuf */ ringbufindex_get(&input_ringbuf); /* Disable further read attempts */ input_frame_buffer->u8PayloadLength = 0; } /* Are we recovering from overflow? */ if(rx_frame_buffer == NULL) { /* get pointer to next input slot */ int put_index = ringbufindex_peek_put(&input_ringbuf); /* is there space? */ if(put_index != -1) { /* move rx_frame_buffer to next empty slot */ rx_frame_buffer = &input_array[put_index]; /* do we need to turn radio on? */ if(MICROMAC_CONF_ALWAYS_ON || missed_radio_on_request) { missed_radio_on_request = 0; on(); } } else { rx_frame_buffer = NULL; } } } PROCESS_END(); } /*---------------------------------------------------------------------------*/ static void set_frame_filtering(uint8_t enable) { frame_filtering = enable; } /*---------------------------------------------------------------------------*/ static void set_autoack(uint8_t enable) { autoack_enabled = enable; } /*---------------------------------------------------------------------------*/ static void set_poll_mode(uint8_t enable) { poll_mode = enable; if(poll_mode) { /* Disable interrupts */ vMMAC_EnableInterrupts(NULL); vMMAC_ConfigureInterruptSources(0); } else { /* Initialize and enable interrupts */ /* TODO: enable E_MMAC_INT_RX_HEADER & filter out frames after header rx */ vMMAC_ConfigureInterruptSources( E_MMAC_INT_RX_COMPLETE | E_MMAC_INT_TX_COMPLETE); vMMAC_EnableInterrupts(&radio_interrupt_handler); } } /* Enable or disable CCA before sending */ static void set_send_on_cca(uint8_t enable) { send_on_cca = enable; } /*---------------------------------------------------------------------------*/ static radio_result_t get_value(radio_param_t param, radio_value_t *value) { if(!value) { return RADIO_RESULT_INVALID_VALUE; } switch(param) { case RADIO_PARAM_POWER_MODE: *value = listen_on || tx_in_progress ? RADIO_POWER_MODE_ON : RADIO_POWER_MODE_OFF; return RADIO_RESULT_OK; case RADIO_PARAM_CHANNEL: *value = get_channel(); return RADIO_RESULT_OK; case RADIO_PARAM_RX_MODE: *value = 0; if(frame_filtering) { *value |= RADIO_RX_MODE_ADDRESS_FILTER; } if(autoack_enabled) { *value |= RADIO_RX_MODE_AUTOACK; } if(poll_mode) { *value |= RADIO_RX_MODE_POLL_MODE; } return RADIO_RESULT_OK; case RADIO_PARAM_TX_MODE: *value = 0; if(send_on_cca) { *value |= RADIO_TX_MODE_SEND_ON_CCA; } return RADIO_RESULT_OK; case RADIO_PARAM_TXPOWER: *value = get_txpower(); return RADIO_RESULT_OK; case RADIO_PARAM_RSSI: *value = get_rssi(); return RADIO_RESULT_OK; case RADIO_PARAM_LAST_RSSI: *value = radio_last_rssi; return RADIO_RESULT_OK; case RADIO_PARAM_CCA_THRESHOLD: *value = cca_thershold; return RADIO_RESULT_OK; case RADIO_CONST_CHANNEL_MIN: *value = 11; return RADIO_RESULT_OK; case RADIO_CONST_CHANNEL_MAX: *value = 26; return RADIO_RESULT_OK; case RADIO_CONST_TXPOWER_MIN: *value = OUTPUT_POWER_MIN; return RADIO_RESULT_OK; case RADIO_CONST_TXPOWER_MAX: *value = OUTPUT_POWER_MAX; return RADIO_RESULT_OK; default: return RADIO_RESULT_NOT_SUPPORTED; } } /*---------------------------------------------------------------------------*/ static radio_result_t set_value(radio_param_t param, radio_value_t value) { switch(param) { case RADIO_PARAM_POWER_MODE: if(value == RADIO_POWER_MODE_ON) { on(); return RADIO_RESULT_OK; } if(value == RADIO_POWER_MODE_OFF) { off(); return RADIO_RESULT_OK; } return RADIO_RESULT_INVALID_VALUE; case RADIO_PARAM_CHANNEL: if(value < 11 || value > 26) { return RADIO_RESULT_INVALID_VALUE; } set_channel(value); return RADIO_RESULT_OK; case RADIO_PARAM_RX_MODE: if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER | RADIO_RX_MODE_AUTOACK | RADIO_RX_MODE_POLL_MODE)) { return RADIO_RESULT_INVALID_VALUE; } set_frame_filtering((value & RADIO_RX_MODE_ADDRESS_FILTER) != 0); set_autoack((value & RADIO_RX_MODE_AUTOACK) != 0); set_poll_mode((value & RADIO_RX_MODE_POLL_MODE) != 0); return RADIO_RESULT_OK; case RADIO_PARAM_TX_MODE: if(value & ~(RADIO_TX_MODE_SEND_ON_CCA)) { return RADIO_RESULT_INVALID_VALUE; } set_send_on_cca((value & RADIO_TX_MODE_SEND_ON_CCA) != 0); return RADIO_RESULT_OK; case RADIO_PARAM_TXPOWER: if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) { return RADIO_RESULT_INVALID_VALUE; /* Find the closest higher PA_LEVEL for the desired output power */ } set_txpower(value); return RADIO_RESULT_OK; case RADIO_PARAM_CCA_THRESHOLD: cca_thershold = value; return RADIO_RESULT_OK; default: return RADIO_RESULT_NOT_SUPPORTED; } } /*---------------------------------------------------------------------------*/ static radio_result_t get_object(radio_param_t param, void *dest, size_t size) { if(param == RADIO_PARAM_LAST_PACKET_TIMESTAMP) { if(size != sizeof(rtimer_clock_t) || !dest) { return RADIO_RESULT_INVALID_VALUE; } *(rtimer_clock_t *)dest = get_packet_timestamp(); return RADIO_RESULT_OK; } return RADIO_RESULT_NOT_SUPPORTED; } /*---------------------------------------------------------------------------*/ static radio_result_t set_object(radio_param_t param, const void *src, size_t size) { return RADIO_RESULT_NOT_SUPPORTED; } /*---------------------------------------------------------------------------*/ const struct radio_driver micromac_radio_driver = { init, prepare, transmit, send, read, cca, receiving_packet, pending_packet, on, off, get_value, set_value, get_object, set_object };