Change API to require all calls be from the same context. (#78)

* Remove atomic driver and all uses of otPlatAtomic* in OpenThread.
* Change posix example to only use a single thread of execution.
This commit is contained in:
Jonathan Hui
2016-05-27 09:16:04 -07:00
parent 21e0e8aa92
commit 7d2e4a0267
26 changed files with 410 additions and 591 deletions
-1
View File
@@ -44,7 +44,6 @@ soc_LDADD = \
$(top_builddir)/src/cli/libopenthread-cli.a \
$(top_builddir)/examples/platform/posix/libopenthread-posix.a \
$(top_builddir)/third_party/mbedtls/libmbedcrypto.a \
-lpthread \
$(NULL)
soc_SOURCES = \
+2 -16
View File
@@ -32,7 +32,6 @@
#include <openthread.h>
#include <cli/cli_serial.hpp>
#include <platform/atomic.h>
#include <platform.h>
struct gengetopt_args_info args_info;
@@ -45,32 +44,19 @@ void otSignalTaskletPending(void)
int main(int argc, char *argv[])
{
uint32_t atomic_state;
if (cmdline_parser(argc, argv, &args_info) != 0)
{
exit(1);
}
hwAlarmInit();
hwRadioInit();
hwRandomInit();
PlatformInit();
otInit();
sCliServer.Start();
while (1)
{
otProcessNextTasklet();
atomic_state = otPlatAtomicBegin();
if (!otAreTaskletsPending())
{
hwSleep();
}
otPlatAtomicEnd(atomic_state);
PlatformProcessDrivers();
}
return 0;
-1
View File
@@ -45,7 +45,6 @@ ncp_LDADD = \
$(top_builddir)/src/core/libopenthread.a \
$(top_builddir)/examples/platform/posix/libopenthread-posix.a \
$(top_builddir)/third_party/mbedtls/libmbedcrypto.a \
-lpthread \
$(NULL)
ncp_SOURCES = main.cpp
+2 -18
View File
@@ -30,7 +30,6 @@
#include <platform/posix/cmdline.h>
#include <ncp/ncp.hpp>
#include <platform/atomic.h>
#include <platform.h>
struct gengetopt_args_info args_info;
@@ -43,19 +42,12 @@ void otSignalTaskletPending(void)
int main(int argc, char *argv[])
{
uint32_t atomic_state;
memset(&args_info, 0, sizeof(args_info));
if (cmdline_parser(argc, argv, &args_info) != 0)
{
exit(1);
}
hwAlarmInit();
hwRadioInit();
hwRandomInit();
PlatformInit();
otInit();
sNcp.Start();
@@ -63,15 +55,7 @@ int main(int argc, char *argv[])
while (1)
{
otProcessNextTasklet();
atomic_state = otPlatAtomicBegin();
if (!otAreTaskletsPending())
{
hwSleep();
}
otPlatAtomicEnd(atomic_state);
PlatformProcessDrivers();
}
return 0;
+1 -1
View File
@@ -52,9 +52,9 @@ libopenthread_posix_a_CXXFLAGS = \
libopenthread_posix_a_SOURCES = \
alarm.c \
atomic.c \
cmdline.c \
logging.c \
platform.c \
radio.cpp \
random.c \
serial.c \
+32 -47
View File
@@ -26,7 +26,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
@@ -34,20 +33,13 @@
#include <platform/alarm.h>
static void *alarm_thread(void *arg);
static bool s_is_running = false;
static uint32_t s_alarm = 0;
static struct timeval s_start;
static pthread_t s_thread;
static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t s_cond = PTHREAD_COND_INITIALIZER;
void hwAlarmInit(void)
void PlatformAlarmInit(void)
{
gettimeofday(&s_start, NULL);
pthread_create(&s_thread, NULL, alarm_thread, NULL);
}
uint32_t otPlatAlarmGetNow(void)
@@ -62,65 +54,58 @@ uint32_t otPlatAlarmGetNow(void)
void otPlatAlarmStartAt(uint32_t t0, uint32_t dt)
{
pthread_mutex_lock(&s_mutex);
s_alarm = t0 + dt;
s_is_running = true;
pthread_mutex_unlock(&s_mutex);
pthread_cond_signal(&s_cond);
}
void otPlatAlarmStop(void)
{
pthread_mutex_lock(&s_mutex);
s_is_running = false;
pthread_mutex_unlock(&s_mutex);
}
void *alarm_thread(void *arg)
void PlatformAlarmUpdateTimeout(struct timeval *aTimeout)
{
int32_t remaining;
struct timeval tva;
struct timeval tvb;
struct timespec ts;
while (1)
if (aTimeout == NULL)
{
pthread_mutex_lock(&s_mutex);
return;
}
if (!s_is_running)
if (s_is_running)
{
remaining = s_alarm - otPlatAlarmGetNow();
if (remaining > 0)
{
// alarm is not running, wait indefinitely
pthread_cond_wait(&s_cond, &s_mutex);
pthread_mutex_unlock(&s_mutex);
aTimeout->tv_sec = remaining / 1000;
aTimeout->tv_usec = (remaining % 1000) * 1000;
}
else
{
// alarm is running
remaining = s_alarm - otPlatAlarmGetNow();
aTimeout->tv_sec = 0;
aTimeout->tv_usec = 0;
}
}
else
{
aTimeout->tv_sec = 10;
aTimeout->tv_usec = 0;
}
}
if (remaining > 0)
{
// alarm has not passed, wait
gettimeofday(&tva, NULL);
tvb.tv_sec = remaining / 1000;
tvb.tv_usec = (remaining % 1000) * 1000;
timeradd(&tva, &tvb, &tva);
void PlatformAlarmProcess(void)
{
int32_t remaining;
ts.tv_sec = tva.tv_sec;
ts.tv_nsec = tva.tv_usec * 1000;
if (s_is_running)
{
remaining = s_alarm - otPlatAlarmGetNow();
pthread_cond_timedwait(&s_cond, &s_mutex, &ts);
pthread_mutex_unlock(&s_mutex);
}
else
{
// alarm has passed, signal
s_is_running = false;
pthread_mutex_unlock(&s_mutex);
otPlatAlarmSignalFired();
}
if (remaining <= 0)
{
s_is_running = false;
otPlatAlarmFired();
}
}
return NULL;
}
-53
View File
@@ -1,53 +0,0 @@
/*
* 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 <pthread.h>
#include <stdio.h>
#include <platform/atomic.h>
#include <platform/posix/platform.h>
static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t s_cond = PTHREAD_COND_INITIALIZER;
uint32_t otPlatAtomicBegin(void)
{
pthread_mutex_lock(&s_mutex);
return 0;
}
void otPlatAtomicEnd(uint32_t state)
{
pthread_mutex_unlock(&s_mutex);
pthread_cond_signal(&s_cond);
}
void hwSleep(void)
{
pthread_cond_wait(&s_cond, &s_mutex);
}
@@ -29,46 +29,48 @@
/**
* @file
* @brief
* This file includes the platform abstraction to support critical sections.
* This file includes the platform-specific initializers.
*/
#ifndef ATOMIC_H_
#define ATOMIC_H_
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/time.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <openthread.h>
#include <platform/alarm.h>
#include "platform.h"
/**
* @defgroup atomic Atomic
* @ingroup platform
*
* @brief
* This module includes the platform abstraction to support critical sections.
*
* @{
*
*/
void PlatformInit(void)
{
PlatformAlarmInit();
PlatformRadioInit();
PlatformRandomInit();
}
/**
* Begin critical section.
*/
uint32_t otPlatAtomicBegin(void);
void PlatformProcessDrivers(void)
{
fd_set read_fds;
fd_set write_fds;
int max_fd = -1;
struct timeval timeout;
int rval;
/**
* End critical section.
*/
void otPlatAtomicEnd(uint32_t state);
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
/**
* @}
*
*/
PlatformSerialUpdateFdSet(&read_fds, &write_fds, &max_fd);
PlatformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd);
PlatformAlarmUpdateTimeout(&timeout);
#ifdef __cplusplus
} // end of extern "C"
#endif
if (!otAreTaskletsPending())
{
rval = select(max_fd + 1, &read_fds, &write_fds, NULL, &timeout);
assert(rval >= 0 && errno != ETIME);
}
#endif // ATOMIC_H_
PlatformSerialProcess();
PlatformRadioProcess();
PlatformAlarmProcess();
}
+64 -4
View File
@@ -36,34 +36,94 @@
#define PLATFORM_H_
#include <stdint.h>
#include <sys/select.h>
#include <sys/time.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* This method performs all platform-specific initialization.
*
*/
void PlatformInit(void);
/**
* This method performs all platform-specific processing.
*
*/
void PlatformProcessDrivers(void);
/**
* This method initializes the alarm service used by OpenThread.
*
*/
void hwAlarmInit(void);
void PlatformAlarmInit(void);
/**
* This method retrieves the time remaining until the alarm fires.
*
* @param[out] aTimeval A pointer to the timeval struct.
*
*/
void PlatformAlarmUpdateTimeout(struct timeval *tv);
/**
* This method performs alarm driver processing.
*
*/
void PlatformAlarmProcess(void);
/**
* This method initializes the radio service used by OpenThread.
*
*/
void hwRadioInit(void);
void PlatformRadioInit(void);
/**
* This method updates the file descriptor sets with file descriptors used by the radio driver.
*
* @param[inout] aReadFdSet A pointer to the read file descriptors.
* @param[inout] aWriteFdSet A pointer to the write file descriptors.
* @param[inout] aMaxFd A pointer to the max file descriptor.
*
*/
void PlatformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd);
/**
* This method performs radio driver processing.
*
*/
void PlatformRadioProcess(void);
/**
* This method initializes the random number service used by OpenThread.
*
*/
void hwRandomInit(void);
void PlatformRandomInit(void);
/**
* This method updates the file descriptor sets with file descriptors used by the serial driver.
*
* @param[inout] aReadFdSet A pointer to the read file descriptors.
* @param[inout] aWriteFdSet A pointer to the write file descriptors.
* @param[inout] aMaxFd A pointer to the max file descriptor.
*
*/
void PlatformSerialUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd);
/**
* This method performs radio driver processing.
*
*/
void PlatformSerialProcess(void);
/**
* This method puts the thread executing OpenThread to sleep.
*
*/
void hwSleep(void);
void PlatformSleep(void);
#ifdef __cplusplus
} // extern "C"
+168 -186
View File
@@ -28,8 +28,8 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <netinet/in.h>
#include <poll.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -70,7 +70,8 @@ struct RadioMessage
uint8_t mPsdu[Mac::Frame::kMTU];
} __attribute__((packed));
static void *phy_receive_thread(void *arg);
static void radioSendAck(void);
static void radioProcessFrame(void);
static PhyState s_state = kStateDisabled;
static RadioPacket *s_receive_frame = NULL;
@@ -82,9 +83,6 @@ 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)
@@ -109,7 +107,7 @@ ThreadError otPlatRadioSetShortAddress(uint16_t address)
return kThreadError_None;
}
void hwRadioInit()
void PlatformRadioInit(void)
{
struct sockaddr_in sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
@@ -119,58 +117,44 @@ void hwRadioInit()
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 otPlatRadioEnable(void)
{
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()
ThreadError otPlatRadioDisable(void)
{
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 otPlatRadioSleep(void)
{
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 otPlatRadioIdle(void)
{
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:
@@ -180,7 +164,6 @@ ThreadError otPlatRadioIdle()
case kStateTransmit:
case kStateAckWait:
s_state = kStateIdle;
pthread_cond_signal(&s_condition_variable);
break;
case kStateDisabled:
@@ -189,7 +172,6 @@ ThreadError otPlatRadioIdle()
}
exit:
pthread_mutex_unlock(&s_mutex);
return error;
}
@@ -197,32 +179,135 @@ 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;
exit:
return error;
}
int8_t otPlatRadioGetNoiseFloor(void)
{
return 0;
}
otRadioCaps otPlatRadioGetCaps(void)
{
return kRadioCapsNone;
}
ThreadError otPlatRadioHandleTransmitDone(bool *rxPending)
{
ThreadError error = kThreadError_None;
VerifyOrExit(s_state == kStateTransmit || s_state == kStateAckWait, error = kThreadError_InvalidState);
s_state = kStateIdle;
if (rxPending != NULL)
{
*rxPending = s_data_pending;
}
exit:
return error;
}
void radioReceive(void)
{
RadioPacket receive_frame;
RadioMessage message;
uint8_t tx_sequence, rx_sequence;
uint8_t command_id;
int rval;
VerifyOrExit(s_state == kStateDisabled || s_state == kStateSleep || s_state == kStateListen ||
s_state == kStateAckWait, ;);
rval = recvfrom(s_sockfd, &message, sizeof(message), 0, NULL, NULL);
assert(rval >= 0);
switch (s_state)
{
case kStateDisabled:
case kStateSleep:
case kStateIdle:
case kStateTransmit:
break;
case kStateAckWait:
receive_frame.mLength = rval - 1;
memcpy(receive_frame.mPsdu, message.mPsdu, receive_frame.mLength);
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;
}
}
s_state = kStateIdle;
otPlatRadioTransmitDone(s_data_pending, kThreadError_None);
break;
case kStateListen:
if (s_receive_frame->mChannel != message.mChannel)
{
break;
}
s_state = kStateReceive;
s_receive_frame->mLength = rval - 1;
memcpy(s_receive_frame->mPsdu, message.mPsdu, s_receive_frame->mLength);
radioProcessFrame();
break;
case kStateReceive:
assert(false);
break;
}
exit:
return;
}
int radioTransmit(void)
{
struct sockaddr_in sockaddr;
RadioMessage message;
int rval;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr);
@@ -238,7 +323,9 @@ ThreadError otPlatRadioTransmit(RadioPacket *packet)
}
sockaddr.sin_port = htons(9000 + i);
sendto(s_sockfd, &message, 1 + s_transmit_frame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
rval = sendto(s_sockfd, &message, 1 + s_transmit_frame->mLength, 0, (struct sockaddr *)&sockaddr,
sizeof(sockaddr));
assert(rval >= 0);
}
if (reinterpret_cast<Mac::Frame *>(s_transmit_frame)->GetAckRequest())
@@ -247,154 +334,56 @@ ThreadError otPlatRadioTransmit(RadioPacket *packet)
}
else
{
otPlatRadioSignalTransmitDone();
s_state = kStateIdle;
otPlatRadioTransmitDone(false, kThreadError_None);
}
exit:
pthread_mutex_unlock(&s_mutex);
return error;
return rval;
}
int8_t otPlatRadioGetNoiseFloor()
void PlatformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd)
{
if (aReadFdSet != NULL &&
(s_state == kStateDisabled || s_state == kStateSleep || s_state == kStateListen || s_state == kStateAckWait))
{
FD_SET(s_sockfd, aReadFdSet);
if (aMaxFd != NULL && *aMaxFd < s_sockfd)
{
*aMaxFd = s_sockfd;
}
}
if (aWriteFdSet != NULL && s_state == kStateTransmit)
{
FD_SET(s_sockfd, aWriteFdSet);
if (aMaxFd != NULL && *aMaxFd < s_sockfd)
{
*aMaxFd = s_sockfd;
}
}
}
int PlatformRadioProcess(void)
{
const int flags = POLLRDNORM | POLLERR | POLLNVAL | POLLHUP;
struct pollfd pollfd = { s_sockfd, flags, 0 };
if (poll(&pollfd, 1, 0) > 0 && (pollfd.revents & flags) != 0)
{
radioReceive();
}
if (s_state == kStateTransmit)
{
radioTransmit();
}
return 0;
}
otRadioCaps otPlatRadioGetCaps()
{
return kRadioCapsNone;
}
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()
void radioSendAck(void)
{
Mac::Frame *ack_frame;
RadioMessage message;
@@ -426,17 +415,14 @@ void send_ack()
}
}
ThreadError otPlatRadioHandleReceiveDone()
void radioProcessFrame(void)
{
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)
@@ -468,21 +454,17 @@ ThreadError otPlatRadioHandleReceiveDone()
// generate acknowledgment
if (reinterpret_cast<Mac::Frame *>(s_receive_frame)->GetAckRequest())
{
send_ack();
radioSendAck();
}
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;
otPlatRadioReceiveDone(error);
}
#ifdef __cplusplus
+1 -1
View File
@@ -41,7 +41,7 @@ extern struct gengetopt_args_info args_info;
static uint32_t s_state = 1;
void hwRandomInit(void)
void PlatformRandomInit(void)
{
s_state = args_info.nodeid_arg;
}
+39 -44
View File
@@ -26,9 +26,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <poll.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
@@ -37,8 +37,6 @@
#include <platform/posix/cmdline.h>
#include <platform/serial.h>
static void *serial_receive_thread(void *arg);
#ifdef OPENTHREAD_TARGET_LINUX
int posix_openpt(int oflag);
int grantpt(int fildes);
@@ -49,20 +47,20 @@ char *ptsname(int fd);
extern struct gengetopt_args_info args_info;
static uint8_t s_receive_buffer[128];
static const uint8_t *s_write_buffer;
static uint16_t s_write_length;
static int s_in_fd;
static int s_out_fd;
static pthread_t s_pthread;
static sem_t *s_semaphore;
static struct termios original_stdin_termios;
static struct termios original_stdout_termios;
static void restore_stdin_termios()
static void restore_stdin_termios(void)
{
tcsetattr(s_in_fd, TCSAFLUSH, &original_stdin_termios);
}
static void restore_stdout_termios()
static void restore_stdout_termios(void)
{
tcsetattr(s_out_fd, TCSAFLUSH, &original_stdout_termios);
}
@@ -72,7 +70,6 @@ ThreadError otPlatSerialEnable(void)
ThreadError error = kThreadError_None;
struct termios termios;
char *path;
char cmd[256];
if (args_info.stdserial_given == 1)
{
@@ -171,10 +168,6 @@ ThreadError otPlatSerialEnable(void)
VerifyOrExit(tcsetattr(s_out_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = kThreadError_Error);
}
snprintf(cmd, sizeof(cmd), "thread_serial_semaphore_%d", args_info.nodeid_arg);
s_semaphore = sem_open(cmd, O_CREAT, 0644, 0);
pthread_create(&s_pthread, NULL, &serial_receive_thread, NULL);
return error;
exit:
@@ -197,54 +190,56 @@ ThreadError otPlatSerialSend(const uint8_t *aBuf, uint16_t aBufLength)
{
ThreadError error = kThreadError_None;
VerifyOrExit(write(s_out_fd, aBuf, aBufLength) >= 0, error = kThreadError_Error);
otPlatSerialSignalSendDone();
VerifyOrExit(s_write_length == 0, error = kThreadError_Busy);
s_write_buffer = aBuf;
s_write_length = aBufLength;
exit:
return error;
}
void otPlatSerialHandleSendDone(void)
void PlatformSerialUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd)
{
}
void *serial_receive_thread(void *aContext)
{
fd_set fds;
int rval;
while (1)
if (aReadFdSet != NULL)
{
FD_ZERO(&fds);
FD_SET(s_in_fd, &fds);
FD_SET(s_in_fd, aReadFdSet);
rval = select(s_in_fd + 1, &fds, NULL, NULL, NULL);
if (rval >= 0 && FD_ISSET(s_in_fd, &fds))
if (aMaxFd != NULL && *aMaxFd < s_in_fd)
{
otPlatSerialSignalReceive();
sem_wait(s_semaphore);
*aMaxFd = s_in_fd;
}
}
return NULL;
if (aWriteFdSet != NULL && s_write_length > 0)
{
FD_SET(s_out_fd, aWriteFdSet);
if (aMaxFd != NULL && *aMaxFd < s_out_fd)
{
*aMaxFd = s_out_fd;
}
}
}
const uint8_t *otPlatSerialGetReceivedBytes(uint16_t *aBufLength)
void PlatformSerialProcess(void)
{
size_t length;
const int flags = POLLRDNORM | POLLERR | POLLNVAL | POLLHUP;
struct pollfd pollfd = { s_in_fd, flags, 0 };
int rval;
length = read(s_in_fd, s_receive_buffer, sizeof(s_receive_buffer));
if (aBufLength != NULL)
if (poll(&pollfd, 1, 0) > 0 && (pollfd.revents & flags) != 0)
{
*aBufLength = length;
rval = read(s_in_fd, s_receive_buffer, sizeof(s_receive_buffer));
assert(rval >= 0);
otPlatSerialReceived(s_receive_buffer, rval);
}
return s_receive_buffer;
}
void otPlatSerialHandleReceiveDone(void)
{
sem_post(s_semaphore);
if (s_write_length > 0)
{
rval = write(s_out_fd, s_write_buffer, s_write_length);
assert(rval >= 0);
s_write_length = 0;
otPlatSerialSendDone();
}
}
-1
View File
@@ -29,7 +29,6 @@
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
include_HEADERS = \
atomic.h \
alarm.h \
logging.h \
radio.h \
+2 -2
View File
@@ -71,7 +71,7 @@ void otPlatAlarmStartAt(uint32_t aT0, uint32_t aDt);
void otPlatAlarmStop(void);
/**
* Get the current current time.
* Get the current time.
*
* @returns The current time in milliseconds.
*/
@@ -80,7 +80,7 @@ uint32_t otPlatAlarmGetNow(void);
/**
* Signal that the alarm has fired.
*/
extern void otPlatAlarmSignalFired(void);
extern void otPlatAlarmFired(void);
/**
* @}
+12 -35
View File
@@ -208,9 +208,6 @@ ThreadError otPlatRadioIdle(void);
* 2. Remain in Receive until a packet is received or reception is aborted.
* 3. Return to Idle.
*
* Upon completion of the receive sequence, otPlatRadioSignalReceiveDone() is called to signal completion to the MAC
* layer.
*
* @param[in] aPacket A pointer to a packet buffer.
*
* @note The channel is specified in @p aPacket.
@@ -221,21 +218,13 @@ ThreadError otPlatRadioIdle(void);
ThreadError otPlatRadioReceive(RadioPacket *aPacket);
/**
* Signal that a packet has been received.
* The radio driver calls this method to notify OpenThread of a received packet.
*
* This may be called from interrupt context. The MAC layer will then schedule a call to otPlatRadioHandleReceive().
*/
extern void otPlatRadioSignalReceiveDone(void);
/**
* Complete the receive sequence.
* @param[in] aError ::kThreadError_None when successfully received a frame, ::kThreadError_Abort when reception
* was aborted and a frame was not received.
*
* @retval ::kThreadError_None Successfully received a frame.
* @retval ::kThreadError_Abort Reception was aborted and a frame was not received.
* @retval ::kThreadError_InvalidState The radio was not in Receive.
*/
ThreadError otPlatRadioHandleReceiveDone(void);
extern void otPlatRadioReceiveDone(ThreadError error);
/**
* Begins the transmit sequence on the radio.
@@ -245,9 +234,6 @@ ThreadError otPlatRadioHandleReceiveDone(void);
* 2. Transmits the psdu on the given channel and at the given transmit power.
* 3. Return to Idle.
*
* Upon completion of the transmit sequence, otPlatRadioSignalTransmitDone() is called to signal completion to the MAC
* layer.
*
* @param[in] aPacket A pointer to a packet buffer.
*
* @note The channel is specified in @p aPacket.
@@ -260,25 +246,16 @@ ThreadError otPlatRadioHandleReceiveDone(void);
ThreadError otPlatRadioTransmit(RadioPacket *aPacket);
/**
* Signal that the requested transmission is complete.
* The radio driver calls this method to notify OpenThread that the transmission has completed.
*
* @param[in] aFramePending TRUE if an ACK frame was received and the Frame Pending bit was set.
* @param[in] aError ::kThreadError_None when the frame was transmitted, ::kThreadError_NoAck when the frame was
* transmitted but no ACK was received, ::kThreadError_ChannelAccessFailure when the transmission
* could not take place due to activity on the channel, ::kThreadError_Abort when transmission was
* aborted for other reasons.
*
* This may be called from interrupt context. OpenThread will then schedule a call to
* otPlatRadio_handle_transmit_done().
*/
extern void otPlatRadioSignalTransmitDone(void);
/**
* Complete the transmit sequence on the radio.
*
* @param[out] aFramePending TRUE if an ACK frame was received and the Frame Pending bit was set.
*
* @retval ::kThreadError_None The frame was transmitted.
* @retval ::kThreadError_NoAck The frame was transmitted, but no ACK was received.
* @retval ::kThreadError_CcaFailed The transmission was aborted due to CCA failure.
* @retval ::kThreadError_Abort The transmission was aborted for other reasons.
* @retval ::kThreadError_InvalidState The radio did not transmit a packet.
*/
ThreadError otPlatRadioHandleTransmitDone(bool *aFramePending);
extern void otPlatRadioTransmitDone(bool aFramePending, ThreadError error);
/**
* Get the most recent RSSI measurement.
+6 -25
View File
@@ -82,38 +82,19 @@ ThreadError otPlatSerialDisable(void);
ThreadError otPlatSerialSend(const uint8_t *aBuf, uint16_t aBufLength);
/**
* Signal that the bytes send operation has completed.
* The serial driver calls this method to notify OpenThread that the requested bytes have been sent.
*
* This may be called from interrupt context. This will schedule calls to otPlatSerialHandleSendDone().
*/
extern void otPlatSerialSignalSendDone(void);
extern void otPlatSerialSendDone(void);
/**
* Complete the send sequence.
*/
void otPlatSerialHandleSendDone(void);
/**
* Signal that bytes have been received.
* The serial driver calls this method to notify OpenThread that bytes have been received.
*
* This may be called from interrupt context. This will schedule calls to otPlatSerialGetReceivedBytes() and
* otPlatSerialHandleReceiveDone().
*/
extern void otPlatSerialSignalReceive(void);
/**
* Get a pointer to the received bytes.
* @param[in] aBuf A pointer to the received bytes.
* @param[in] aBufLength The number of bytes received.
*
* @param[out] aBufLength A pointer to a variable that this function will put the number of bytes received.
*
* @returns A pointer to the received bytes. NULL, if there are no received bytes to process.
*/
const uint8_t *otPlatSerialGetReceivedBytes(uint16_t *aBufLength);
/**
* Release received bytes.
*/
void otPlatSerialHandleReceiveDone(void);
extern void otPlatSerialReceived(const uint8_t *aBuf, uint16_t aBufLength);
/**
* @}
+9 -27
View File
@@ -50,9 +50,6 @@ static const char sEraseString[] = {'\b', ' ', '\b'};
static const char CRNL[] = {'\r', '\n'};
static Serial *sServer;
Tasklet Serial::sReceiveTask(&ReceiveTask, NULL);
Tasklet Serial::sSendDoneTask(&SendDoneTask, NULL);
Serial::Serial(void)
{
sServer = this;
@@ -68,28 +65,20 @@ ThreadError Serial::Start(void)
return kThreadError_None;
}
extern "C" void otPlatSerialSignalReceive(void)
extern "C" void otPlatSerialReceived(const uint8_t *aBuf, uint16_t aBufLength)
{
Serial::sReceiveTask.Post();
sServer->ReceiveTask(aBuf, aBufLength);
}
void Serial::ReceiveTask(void *aContext)
void Serial::ReceiveTask(const uint8_t *aBuf, uint16_t aBufLength)
{
sServer->ReceiveTask();
}
void Serial::ReceiveTask(void)
{
uint16_t bufLength;
const uint8_t *buf;
const uint8_t *end;
buf = otPlatSerialGetReceivedBytes(&bufLength);
end = buf + bufLength;
end = aBuf + aBufLength;
for (; buf < end; buf++)
for (; aBuf < end; aBuf++)
{
switch (*buf)
switch (*aBuf)
{
case '\r':
case '\n':
@@ -115,13 +104,11 @@ void Serial::ReceiveTask(void)
break;
default:
Output(reinterpret_cast<const char *>(buf), 1);
mRxBuffer[mRxLength++] = *buf;
Output(reinterpret_cast<const char *>(aBuf), 1);
mRxBuffer[mRxLength++] = *aBuf;
break;
}
}
otPlatSerialHandleReceiveDone();
}
ThreadError Serial::ProcessCommand(void)
@@ -201,12 +188,7 @@ exit:
return;
}
extern "C" void otPlatSerialSignalSendDone(void)
{
Serial::sSendDoneTask.Post();
}
void Serial::SendDoneTask(void *aContext)
extern "C" void otPlatSerialSendDone(void)
{
sServer->SendDoneTask();
}
+2 -7
View File
@@ -80,8 +80,8 @@ public:
*/
int OutputFormat(const char *fmt, ...);
static Tasklet sReceiveTask;
static Tasklet sSendDoneTask;
void ReceiveTask(const uint8_t *aBuf, uint16_t aBufLength);
void SendDoneTask(void);
private:
enum
@@ -91,12 +91,7 @@ private:
kMaxLineLength = 128,
};
static void ReceiveTask(void *aContext);
static void SendDoneTask(void *aContext);
ThreadError ProcessCommand(void);
void ReceiveTask(void);
void SendDoneTask(void);
void Send(void);
char mRxBuffer[kRxBufferSize];
+1 -1
View File
@@ -80,7 +80,7 @@ public:
private:
enum
{
kMaxLineLength = 80,
kMaxLineLength = 128,
};
static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo);
-7
View File
@@ -35,7 +35,6 @@
#include <common/code_utils.hpp>
#include <common/debug.hpp>
#include <common/tasklet.hpp>
#include <platform/atomic.h>
namespace Thread {
@@ -57,7 +56,6 @@ ThreadError Tasklet::Post(void)
ThreadError TaskletScheduler::Post(Tasklet &aTasklet)
{
ThreadError error = kThreadError_None;
uint32_t state = otPlatAtomicBegin();
VerifyOrExit(sTail != &aTasklet && aTasklet.mNext == NULL, error = kThreadError_Busy);
@@ -74,8 +72,6 @@ ThreadError TaskletScheduler::Post(Tasklet &aTasklet)
}
exit:
otPlatAtomicEnd(state);
return error;
}
@@ -105,12 +101,9 @@ bool TaskletScheduler::AreTaskletsPending(void)
void TaskletScheduler::RunNextTasklet(void)
{
uint32_t state;
Tasklet *task;
state = otPlatAtomicBegin();
task = PopTasklet();
otPlatAtomicEnd(state);
if (task != NULL)
{
+4 -12
View File
@@ -37,7 +37,6 @@
namespace Thread {
static Tasklet sTask(&TimerScheduler::FireTimers, NULL);
static Timer *sHead = NULL;
static Timer *sTail = NULL;
@@ -144,25 +143,18 @@ void TimerScheduler::SetAlarm(void)
}
}
if (minRemaining <= 0)
{
sTask.Post();
}
else
{
otPlatAlarmStartAt(now, minRemaining);
}
otPlatAlarmStartAt(now, minRemaining);
exit:
{}
}
extern "C" void otPlatAlarmSignalFired(void)
extern "C" void otPlatAlarmFired(void)
{
Thread::sTask.Post();
TimerScheduler::FireTimers();
}
void TimerScheduler::FireTimers(void *aContext)
void TimerScheduler::FireTimers(void)
{
uint32_t now = otPlatAlarmGetNow();
uint32_t elapsed;
+1 -1
View File
@@ -95,7 +95,7 @@ public:
* @param[in] aContext A pointer to arbitrary context information.
*
*/
static void FireTimers(void *aContext);
static void FireTimers(void);
private:
static void SetAlarm(void);
+12 -32
View File
@@ -50,9 +50,6 @@ static const uint8_t sExtendedPanidInit[] = {0xde, 0xad, 0x00, 0xbe, 0xef, 0x00,
static const char sNetworkNameInit[] = "OpenThread";
static Mac *sMac;
static Tasklet sReceiveDoneTask(&Mac::ReceiveDoneTask, NULL);
static Tasklet sTransmitDoneTask(&Mac::TransmitDoneTask, NULL);
void Mac::StartCsmaBackoff(void)
{
uint32_t backoffExponent = kMinBE + mTransmitAttempts + mCsmaAttempts;
@@ -63,8 +60,8 @@ void Mac::StartCsmaBackoff(void)
backoffExponent = kMaxBE;
}
backoff = (kUnitBackoffPeriod * kPhyUsPerSymbol * (1 << backoffExponent)) / 1000;
backoff = (otPlatRandomGet() % backoff) + kMinBackoff;
backoff = kMinBackoff + (kUnitBackoffPeriod * kPhyUsPerSymbol * (1 << backoffExponent)) / 1000;
backoff = (otPlatRandomGet() % backoff);
mBackoffTimer.Start(backoff);
}
@@ -481,26 +478,16 @@ exit:
}
}
extern "C" void otPlatRadioSignalTransmitDone(void)
extern "C" void otPlatRadioTransmitDone(bool aRxPending, ThreadError aError)
{
sTransmitDoneTask.Post();
sMac->TransmitDoneTask(aRxPending, aError);
}
void Mac::TransmitDoneTask(void *aContext)
void Mac::TransmitDoneTask(bool aRxPending, ThreadError aError)
{
sMac->TransmitDoneTask();
}
void Mac::TransmitDoneTask(void)
{
ThreadError error;
bool rxPending;
error = otPlatRadioHandleTransmitDone(&rxPending);
mAckTimer.Stop();
if (error == kThreadError_ChannelAccessFailure &&
if (aError == kThreadError_ChannelAccessFailure &&
mCsmaAttempts < kMaxCSMABackoffs)
{
mCsmaAttempts++;
@@ -521,7 +508,7 @@ void Mac::TransmitDoneTask(void)
break;
case kStateTransmitData:
if (rxPending)
if (aRxPending)
{
mReceiveTimer.Start(kDataPollTimeout);
}
@@ -530,7 +517,7 @@ void Mac::TransmitDoneTask(void)
mReceiveTimer.Stop();
}
SentFrame(error == kThreadError_None);
SentFrame(aError == kThreadError_None);
break;
default:
@@ -752,19 +739,13 @@ exit:
return error;
}
extern "C" void otPlatRadioSignalReceiveDone(void)
extern "C" void otPlatRadioReceiveDone(ThreadError aError)
{
sReceiveDoneTask.Post();
sMac->ReceiveDoneTask(aError);
}
void Mac::ReceiveDoneTask(void *aContext)
void Mac::ReceiveDoneTask(ThreadError aError)
{
sMac->ReceiveDoneTask();
}
void Mac::ReceiveDoneTask(void)
{
ThreadError error;
Address srcaddr;
Address dstaddr;
PanId panid;
@@ -772,8 +753,7 @@ void Mac::ReceiveDoneTask(void)
Whitelist::Entry *entry;
int8_t rssi;
error = otPlatRadioHandleReceiveDone();
VerifyOrExit(error == kThreadError_None, ;);
VerifyOrExit(aError == kThreadError_None, ;);
mReceiveFrame.GetSrcAddr(srcaddr);
neighbor = mMle.GetNeighbor(srcaddr);
+9 -7
View File
@@ -366,18 +366,23 @@ public:
/**
* This method is called to handle receive events.
*
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aError ::kThreadError_None when successfully received a frame, ::kThreadError_Abort when reception
* was aborted and a frame was not received.
*
*/
static void ReceiveDoneTask(void *aContext);
void ReceiveDoneTask(ThreadError aError);
/**
* This method is called to handle transmit events.
*
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aFramePending TRUE if an ACK frame was received and the Frame Pending bit was set.
* @param[in] aError ::kThreadError_None when the frame was transmitted, ::kThreadError_NoAck when the frame was
* transmitted but no ACK was received, ::kThreadError_ChannelAccessFailure when the transmission
* could not take place due to activity on the channel, ::kThreadError_Abort when transmission
* was aborted for other reasons.
*
*/
static void TransmitDoneTask(void *aContext);
void TransmitDoneTask(bool aRxPending, ThreadError aError);
/**
* This method returns if an active scan is in progress.
@@ -406,9 +411,6 @@ private:
void StartCsmaBackoff(void);
void ReceiveDoneTask(void);
void TransmitDoneTask(void);
Tasklet mBeginTransmit;
Timer mAckTimer;
Timer mBackoffTimer;
+6 -25
View File
@@ -36,8 +36,6 @@
namespace Thread {
static Tasklet sSendDoneTask(&Ncp::SendDoneTask, NULL);
static Tasklet sReceiveTask(&Ncp::ReceiveTask, NULL);
static Ncp *sNcp;
Ncp::Ncp():
@@ -146,17 +144,12 @@ Ncp::OutboundFrameSend(void)
return errorCode;
}
extern "C" void otPlatSerialSignalSendDone()
{
sSendDoneTask.Post();
}
void Ncp::SendDoneTask(void *context)
extern "C" void otPlatSerialSendDone(void)
{
sNcp->SendDoneTask();
}
void Ncp::SendDoneTask()
void Ncp::SendDoneTask(void)
{
mSending = false;
@@ -169,26 +162,14 @@ void Ncp::SendDoneTask()
super_t::HandleSendDone();
}
extern "C" void otPlatSerialSignalReceive()
extern "C" void otPlatSerialReceived(const uint8_t *aBuf, uint16_t aBufLength)
{
sReceiveTask.Post();
sNcp->ReceiveTask(aBuf, aBufLength);
}
void Ncp::ReceiveTask(void *context)
void Ncp::ReceiveTask(const uint8_t *aBuf, uint16_t aBufLength)
{
sNcp->ReceiveTask();
}
void Ncp::ReceiveTask()
{
const uint8_t *buf;
uint16_t bufLength;
buf = otPlatSerialGetReceivedBytes(&bufLength);
mFrameDecoder.Decode(buf, bufLength);
otPlatSerialHandleReceiveDone();
mFrameDecoder.Decode(aBuf, aBufLength);
}
void Ncp::HandleFrame(void *context, uint8_t *aBuf, uint16_t aBufLength)
+2 -4
View File
@@ -57,13 +57,11 @@ public:
virtual ThreadError OutboundFrameSend(void);
static void HandleFrame(void *context, uint8_t *aBuf, uint16_t aBufLength);
static void SendDoneTask(void *context);
static void ReceiveTask(void *context);
void SendDoneTask(void);
void ReceiveTask(const uint8_t *aBuf, uint16_t aBufLength);
private:
void HandleFrame(uint8_t *aBuf, uint16_t aBufLength);
void SendDoneTask();
void ReceiveTask();
Hdlc::Encoder mFrameEncoder;
Hdlc::Decoder mFrameDecoder;