mirror of
https://github.com/espressif/openthread.git
synced 2026-07-11 06:40:19 +00:00
1f24fa44fa
* Add active scan functions and definitions to core C api. * Add active scan command to CLI. * Add emulation of PHY channel isolation.
487 lines
12 KiB
C++
487 lines
12 KiB
C++
/*
|
|
* 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
|