blob: 40a9ffce4730abd3febeeb6cb68d911d3c66ba45 [file] [log] [blame]
/*
* Copyright (c) 2016, Nest Labs, Inc.
* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
*/
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <platform/posix/cmdline.h>
#include <common/code_utils.hpp>
#include <common/debug.hpp>
#include <mac/mac.hpp>
#include <platform/radio.h>
#ifdef __cplusplus
extern "C" {
#endif
extern struct gengetopt_args_info args_info;
namespace Thread {
enum PhyState
{
kStateDisabled = 0,
kStateSleep = 1,
kStateIdle = 2,
kStateListen = 3,
kStateReceive = 4,
kStateTransmit = 5,
kStateAckWait = 6,
};
struct RadioMessage
{
uint8_t mChannel;
uint8_t mPsdu[Mac::Frame::kMTU];
} __attribute__((packed));
static void *phy_receive_thread(void *arg);
static PhyState s_state = kStateDisabled;
static RadioPacket *s_receive_frame = NULL;
static RadioPacket *s_transmit_frame = NULL;
static RadioPacket m_ack_packet;
static bool s_data_pending = false;
static uint8_t s_extended_address[8];
static uint16_t s_short_address;
static uint16_t s_panid;
static pthread_t s_pthread;
static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t s_condition_variable = PTHREAD_COND_INITIALIZER;
static int s_sockfd;
ThreadError otPlatRadioSetPanId(uint16_t panid)
{
s_panid = panid;
return kThreadError_None;
}
ThreadError otPlatRadioSetExtendedAddress(uint8_t *address)
{
for (unsigned i = 0; i < sizeof(s_extended_address); i++)
{
s_extended_address[i] = address[7 - i];
}
return kThreadError_None;
}
ThreadError otPlatRadioSetShortAddress(uint16_t address)
{
s_short_address = address;
return kThreadError_None;
}
void hwRadioInit()
{
struct sockaddr_in sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(9000 + args_info.nodeid_arg);
sockaddr.sin_addr.s_addr = INADDR_ANY;
s_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bind(s_sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
pthread_create(&s_pthread, NULL, &phy_receive_thread, NULL);
}
ThreadError otPlatRadioEnable()
{
ThreadError error = kThreadError_None;
pthread_mutex_lock(&s_mutex);
VerifyOrExit(s_state == kStateDisabled, error = kThreadError_Busy);
s_state = kStateSleep;
pthread_cond_signal(&s_condition_variable);
exit:
pthread_mutex_unlock(&s_mutex);
return error;
}
ThreadError otPlatRadioDisable()
{
pthread_mutex_lock(&s_mutex);
s_state = kStateDisabled;
pthread_cond_signal(&s_condition_variable);
pthread_mutex_unlock(&s_mutex);
return kThreadError_None;
}
ThreadError otPlatRadioSleep()
{
ThreadError error = kThreadError_None;
pthread_mutex_lock(&s_mutex);
VerifyOrExit(s_state == kStateIdle, error = kThreadError_Busy);
s_state = kStateSleep;
pthread_cond_signal(&s_condition_variable);
exit:
pthread_mutex_unlock(&s_mutex);
return error;
}
ThreadError otPlatRadioIdle()
{
ThreadError error = kThreadError_None;
pthread_mutex_lock(&s_mutex);
switch (s_state)
{
case kStateSleep:
s_state = kStateIdle;
pthread_cond_signal(&s_condition_variable);
break;
case kStateIdle:
break;
case kStateListen:
case kStateTransmit:
case kStateAckWait:
s_state = kStateIdle;
pthread_cond_signal(&s_condition_variable);
break;
case kStateDisabled:
case kStateReceive:
ExitNow(error = kThreadError_Busy);
}
exit:
pthread_mutex_unlock(&s_mutex);
return error;
}
ThreadError otPlatRadioReceive(RadioPacket *packet)
{
ThreadError error = kThreadError_None;
pthread_mutex_lock(&s_mutex);
VerifyOrExit(s_state == kStateIdle, error = kThreadError_Busy);
s_state = kStateListen;
pthread_cond_signal(&s_condition_variable);
s_receive_frame = packet;
exit:
pthread_mutex_unlock(&s_mutex);
return error;
}
ThreadError otPlatRadioTransmit(RadioPacket *packet)
{
ThreadError error = kThreadError_None;
struct sockaddr_in sockaddr;
RadioMessage message;
pthread_mutex_lock(&s_mutex);
VerifyOrExit(s_state == kStateIdle, error = kThreadError_Busy);
s_state = kStateTransmit;
pthread_cond_signal(&s_condition_variable);
s_transmit_frame = packet;
s_data_pending = false;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr);
message.mChannel = s_transmit_frame->mChannel;
memcpy(message.mPsdu, s_transmit_frame->mPsdu, s_transmit_frame->mLength);
for (int i = 1; i < 34; i++)
{
if (args_info.nodeid_arg == i)
{
continue;
}
sockaddr.sin_port = htons(9000 + i);
sendto(s_sockfd, &message, 1 + s_transmit_frame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
}
if (reinterpret_cast<Mac::Frame *>(s_transmit_frame)->GetAckRequest())
{
s_state = kStateAckWait;
}
else
{
otPlatRadioSignalTransmitDone();
}
exit:
pthread_mutex_unlock(&s_mutex);
return error;
}
int8_t otPlatRadioGetNoiseFloor()
{
return 0;
}
ThreadError otPlatRadioHandleTransmitDone(bool *rxPending)
{
ThreadError error = kThreadError_None;
VerifyOrExit(s_state == kStateTransmit || s_state == kStateAckWait, error = kThreadError_InvalidState);
pthread_mutex_lock(&s_mutex);
s_state = kStateIdle;
pthread_cond_signal(&s_condition_variable);
pthread_mutex_unlock(&s_mutex);
if (rxPending != NULL)
{
*rxPending = s_data_pending;
}
exit:
return error;
}
void *phy_receive_thread(void *arg)
{
fd_set fds;
int rval;
RadioPacket receive_frame;
int length;
uint8_t tx_sequence, rx_sequence;
uint8_t command_id;
RadioMessage message;
while (1)
{
FD_ZERO(&fds);
FD_SET(s_sockfd, &fds);
rval = select(s_sockfd + 1, &fds, NULL, NULL, NULL);
if (rval < 0 || !FD_ISSET(s_sockfd, &fds))
{
continue;
}
pthread_mutex_lock(&s_mutex);
while (s_state == kStateIdle || s_state == kStateTransmit)
{
pthread_cond_wait(&s_condition_variable, &s_mutex);
}
switch (s_state)
{
case kStateDisabled:
case kStateIdle:
case kStateSleep:
recvfrom(s_sockfd, NULL, 0, 0, NULL, NULL);
break;
case kStateTransmit:
break;
case kStateAckWait:
length = recvfrom(s_sockfd, &message, sizeof(message), 0, NULL, NULL);
receive_frame.mLength = length - 1;
memcpy(receive_frame.mPsdu, message.mPsdu, receive_frame.mLength);
if (length < 0)
{
assert(false);
}
if (reinterpret_cast<Mac::Frame *>(&receive_frame)->GetType() != Mac::Frame::kFcfFrameAck)
{
break;
}
tx_sequence = reinterpret_cast<Mac::Frame *>(s_transmit_frame)->GetSequence();
rx_sequence = reinterpret_cast<Mac::Frame *>(&receive_frame)->GetSequence();
if (tx_sequence != rx_sequence)
{
break;
}
if (reinterpret_cast<Mac::Frame *>(s_transmit_frame)->GetType() == Mac::Frame::kFcfFrameMacCmd)
{
reinterpret_cast<Mac::Frame *>(s_transmit_frame)->GetCommandId(command_id);
if (command_id == Mac::Frame::kMacCmdDataRequest)
{
s_data_pending = true;
}
}
otPlatRadioSignalTransmitDone();
break;
case kStateListen:
length = recvfrom(s_sockfd, &message, sizeof(message), 0, NULL, NULL);
if (s_receive_frame->mChannel != message.mChannel)
{
break;
}
s_state = kStateReceive;
s_receive_frame->mLength = length - 1;
memcpy(s_receive_frame->mPsdu, message.mPsdu, s_receive_frame->mLength);
otPlatRadioSignalReceiveDone();
while (s_state == kStateReceive)
{
pthread_cond_wait(&s_condition_variable, &s_mutex);
}
break;
case kStateReceive:
assert(false);
break;
}
pthread_mutex_unlock(&s_mutex);
}
return NULL;
}
void send_ack()
{
Mac::Frame *ack_frame;
RadioMessage message;
uint8_t sequence;
struct sockaddr_in sockaddr;
sequence = reinterpret_cast<Mac::Frame *>(s_receive_frame)->GetSequence();
ack_frame = reinterpret_cast<Mac::Frame *>(&m_ack_packet);
ack_frame->InitMacHeader(Mac::Frame::kFcfFrameAck, Mac::Frame::kSecNone);
ack_frame->SetSequence(sequence);
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr);
message.mChannel = s_receive_frame->mChannel;
memcpy(message.mPsdu, m_ack_packet.mPsdu, m_ack_packet.mLength);
for (int i = 1; i < 34; i++)
{
if (args_info.nodeid_arg == i)
{
continue;
}
sockaddr.sin_port = htons(9000 + i);
sendto(s_sockfd, &message, 1 + m_ack_packet.mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
}
}
ThreadError otPlatRadioHandleReceiveDone()
{
ThreadError error = kThreadError_None;
Mac::Frame *receive_frame;
uint16_t dstpan;
Mac::Address dstaddr;
VerifyOrExit(s_state == kStateReceive, error = kThreadError_InvalidState);
receive_frame = reinterpret_cast<Mac::Frame *>(s_receive_frame);
receive_frame->GetDstAddr(dstaddr);
switch (dstaddr.mLength)
{
case 0:
break;
case 2:
receive_frame->GetDstPanId(dstpan);
VerifyOrExit((dstpan == Mac::kShortAddrBroadcast || dstpan == s_panid) &&
(dstaddr.mShortAddress == Mac::kShortAddrBroadcast || dstaddr.mShortAddress == s_short_address),
error = kThreadError_Abort);
break;
case 8:
receive_frame->GetDstPanId(dstpan);
VerifyOrExit((dstpan == Mac::kShortAddrBroadcast || dstpan == s_panid) &&
memcmp(&dstaddr.mExtAddress, s_extended_address, sizeof(dstaddr.mExtAddress)) == 0,
error = kThreadError_Abort);
break;
default:
ExitNow(error = kThreadError_Abort);
}
s_receive_frame->mPower = -20;
// generate acknowledgment
if (reinterpret_cast<Mac::Frame *>(s_receive_frame)->GetAckRequest())
{
send_ack();
}
exit:
pthread_mutex_lock(&s_mutex);
if (s_state != kStateDisabled)
{
s_state = kStateIdle;
}
pthread_cond_signal(&s_condition_variable);
pthread_mutex_unlock(&s_mutex);
return error;
}
#ifdef __cplusplus
} // end of extern "C"
#endif
} // namespace Thread