#include "openthread-posix-config.h"
#include "platform-posix.h"
#include <arpa/inet.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <unistd.h>
#if OPENTHREAD_POSIX_VIRTUAL_TIME
static const int kWellKnownNodeId = 34; static const int kBasePort = 18000; static const int kUsPerSecond = 1000000;
static uint64_t sNow = 0; static int sSockFd = -1; static uint16_t sPortOffset = 0;
void virtualTimeInit(uint16_t aNodeId)
{
struct sockaddr_in sockaddr;
char * offset;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
offset = getenv("PORT_OFFSET");
if (offset)
{
char *endptr;
sPortOffset = (uint16_t)strtol(offset, &endptr, 0);
if (*endptr != '\0')
{
const uint8_t kMsgSize = 40;
char msg[kMsgSize];
snprintf(msg, sizeof(msg), "Invalid PORT_OFFSET: %s", offset);
DieNowWithMessage(msg, OT_EXIT_INVALID_ARGUMENTS);
}
sPortOffset *= kWellKnownNodeId;
}
sockaddr.sin_port = htons(kBasePort + sPortOffset + aNodeId);
sockaddr.sin_addr.s_addr = INADDR_ANY;
sSockFd = SocketWithCloseExec(AF_INET, SOCK_DGRAM, IPPROTO_UDP, kSocketBlock);
if (sSockFd == -1)
{
DieNowWithMessage("socket", OT_EXIT_ERROR_ERRNO);
}
if (bind(sSockFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == -1)
{
DieNowWithMessage("bind", OT_EXIT_ERROR_ERRNO);
}
}
void virtualTimeDeinit(void)
{
if (sSockFd != -1)
{
close(sSockFd);
sSockFd = -1;
}
}
static void virtualTimeSendEvent(struct Event *aEvent, size_t aLength)
{
ssize_t rval;
struct sockaddr_in sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr);
sockaddr.sin_port = htons(9000 + sPortOffset);
rval = sendto(sSockFd, aEvent, aLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (rval < 0)
{
DieNowWithMessage("sendto", OT_EXIT_ERROR_ERRNO);
}
}
void virtualTimeReceiveEvent(struct Event *aEvent)
{
ssize_t rval = recvfrom(sSockFd, aEvent, sizeof(*aEvent), 0, NULL, NULL);
if (rval < 0 || (uint16_t)rval < offsetof(struct Event, mData))
{
DieNowWithMessage("recvfrom", (rval < 0) ? OT_EXIT_ERROR_ERRNO : OT_EXIT_FAILURE);
}
sNow += aEvent->mDelay;
}
void virtualTimeSendSleepEvent(const struct timeval *aTimeout)
{
struct Event event;
event.mDelay = (uint64_t)aTimeout->tv_sec * kUsPerSecond + (uint64_t)aTimeout->tv_usec;
event.mEvent = OT_SIM_EVENT_ALARM_FIRED;
event.mDataLength = 0;
virtualTimeSendEvent(&event, offsetof(struct Event, mData));
}
void virtualTimeSendRadioSpinelWriteEvent(const uint8_t *aData, uint16_t aLength)
{
struct Event event;
event.mDelay = 0;
event.mEvent = OT_SIM_EVENT_RADIO_SPINEL_WRITE;
event.mDataLength = aLength;
memcpy(event.mData, aData, aLength);
virtualTimeSendEvent(&event, offsetof(struct Event, mData) + event.mDataLength);
}
void virtualTimeUpdateFdSet(fd_set * aReadFdSet,
fd_set * aWriteFdSet,
fd_set * aErrorFdSet,
int * aMaxFd,
struct timeval *aTimeout)
{
OT_UNUSED_VARIABLE(aWriteFdSet);
OT_UNUSED_VARIABLE(aErrorFdSet);
OT_UNUSED_VARIABLE(aTimeout);
FD_SET(sSockFd, aReadFdSet);
if (*aMaxFd < sSockFd)
{
*aMaxFd = sSockFd;
}
}
void virtualTimeProcess(otInstance * aInstance,
const fd_set *aReadFdSet,
const fd_set *aWriteFdSet,
const fd_set *aErrorFdSet)
{
struct Event event;
memset(&event, 0, sizeof(event));
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aWriteFdSet);
OT_UNUSED_VARIABLE(aErrorFdSet);
if (FD_ISSET(sSockFd, aReadFdSet))
{
virtualTimeReceiveEvent(&event);
}
virtualTimeRadioSpinelProcess(aInstance, &event);
}
uint64_t otPlatTimeGet(void)
{
return sNow;
}
#endif