367 lines
8.8 KiB
C
367 lines
8.8 KiB
C
/*-
|
|
* Copyright (c) 2017-2021 Carsten Sonne Larsen <cs@innolan.net>
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "config.h"
|
|
#include "ptz.h"
|
|
#include "timer.h"
|
|
#include "mem.h"
|
|
#include "tz.h"
|
|
|
|
#include <exec/interrupts.h>
|
|
#include <clib/alib_protos.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include "logmod.h"
|
|
#define MODULENAME "Timer"
|
|
|
|
#define TIMER_ERROR 10
|
|
#define TIMER_OK 0
|
|
|
|
#define TIMER_INTERRUPT_ON 1
|
|
#define TIMER_INTERRUPT_OFF 2
|
|
#define TIMER_INTERRUPT_STOPPED 3
|
|
|
|
struct TimerInfo
|
|
{
|
|
bool InterruptTimer;
|
|
bool Trigger;
|
|
bool Restart;
|
|
long TimerFlag;
|
|
struct timeval Interval;
|
|
long Counter;
|
|
struct MsgPort *TimerPort;
|
|
struct Interrupt *TimerInterrupt;
|
|
struct timerequest *TimerIO;
|
|
struct Task *InterruptTask;
|
|
ULONG InterruptSigBit;
|
|
};
|
|
|
|
struct Device *TimerBase = NULL;
|
|
static struct timerequest *TimerIO = NULL;
|
|
|
|
// Only one interrupt timer is allowed
|
|
struct TimerInfo *InterruptInfo;
|
|
|
|
/*
|
|
* Set up pointer for timer functions.
|
|
*/
|
|
struct Device *OpenTimerBase(void)
|
|
{
|
|
LONG error;
|
|
|
|
TimerIO = AllocStructSafe(struct timerequest);
|
|
if (TimerIO == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
error = OpenDevice((STRPTR)TIMERNAME, UNIT_MICROHZ, (struct IORequest *)TimerIO, 0);
|
|
if (error != 0)
|
|
{
|
|
FreeMemSafe(TimerIO);
|
|
TimerIO = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
TimerBase = TimerIO->tr_node.io_Device;
|
|
return TimerBase;
|
|
}
|
|
|
|
void CloseTimerBase(void)
|
|
{
|
|
if (TimerBase != NULL && TimerIO != NULL)
|
|
{
|
|
CloseDevice((struct IORequest *)TimerIO);
|
|
}
|
|
|
|
if (TimerIO != NULL)
|
|
{
|
|
FreeMemSafe(TimerIO);
|
|
}
|
|
}
|
|
|
|
void SetTime(const struct timeval *tv)
|
|
{
|
|
assert(tv != NULL);
|
|
|
|
// Notice: Method is not thread safe.
|
|
TimerIO->tr_node.io_Command = TR_SETSYSTIME;
|
|
TimerIO->tr_time.tv_secs = (long)tv->tv_secs;
|
|
TimerIO->tr_time.tv_micro = tv->tv_micro;
|
|
DoIO((struct IORequest *)TimerIO);
|
|
}
|
|
|
|
static void TimerInterruptCode(void)
|
|
{
|
|
struct TimerInfo *info;
|
|
struct timerequest *tr;
|
|
|
|
info = InterruptInfo;
|
|
tr = (struct timerequest *)GetMsg(info->TimerPort);
|
|
|
|
if ((tr) && (info->TimerFlag == TIMER_INTERRUPT_ON))
|
|
{
|
|
if (info->Restart)
|
|
{
|
|
info->Counter = 0;
|
|
info->Trigger = false;
|
|
info->Restart = false;
|
|
tr->tr_time.tv_secs = 1;
|
|
tr->tr_time.tv_micro = 0;
|
|
}
|
|
else if (info->Trigger)
|
|
{
|
|
Signal(info->InterruptTask, info->InterruptSigBit);
|
|
info->Counter = 0;
|
|
info->Trigger = false;
|
|
tr->tr_time.tv_secs = 1;
|
|
tr->tr_time.tv_micro = 0;
|
|
}
|
|
else
|
|
{
|
|
info->Counter++;
|
|
if (info->Counter == info->Interval.tv_secs)
|
|
{
|
|
tr->tr_time.tv_secs = 0;
|
|
tr->tr_time.tv_micro = info->Interval.tv_micro;
|
|
info->Trigger = true;
|
|
}
|
|
else
|
|
{
|
|
tr->tr_time.tv_secs = 1;
|
|
tr->tr_time.tv_micro = 0;
|
|
}
|
|
}
|
|
|
|
tr->tr_node.io_Command = TR_ADDREQUEST;
|
|
BeginIO((struct IORequest *)tr);
|
|
}
|
|
else
|
|
{
|
|
info->TimerFlag = TIMER_INTERRUPT_STOPPED;
|
|
}
|
|
}
|
|
|
|
void SetInterruptTimerInterval(struct TimerInfo *info, struct timeval *tv)
|
|
{
|
|
info->Interval.tv_secs = tv->tv_secs;
|
|
info->Interval.tv_micro = tv->tv_micro;
|
|
|
|
if (info->Interval.tv_micro == 0)
|
|
{
|
|
info->Interval.tv_micro = 2;
|
|
}
|
|
info->Restart = true;
|
|
}
|
|
|
|
void StartInterruptTimer(struct TimerInfo *info)
|
|
{
|
|
assert(info != NULL);
|
|
|
|
if (!info->InterruptTimer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
info->TimerFlag = TIMER_INTERRUPT_ON;
|
|
info->Trigger = false;
|
|
info->Restart = false;
|
|
info->TimerIO->tr_node.io_Command = TR_ADDREQUEST;
|
|
info->TimerIO->tr_time.tv_secs = 1;
|
|
info->TimerIO->tr_time.tv_micro = 0;
|
|
BeginIO((struct IORequest *)info->TimerIO);
|
|
}
|
|
|
|
/*
|
|
* Create a Timer device software interrupt as described on wiki.amigaos.net:
|
|
* https://wiki.amigaos.net/wiki/Exec_Interrupts#Software_Interrupts
|
|
* and Amiga Developer Docs 2.1, lib_examples/timersoftint.c
|
|
*/
|
|
struct TimerInfo *CreateInterruptTimer(struct Task *task, short sigBit)
|
|
{
|
|
LONG error;
|
|
struct TimerInfo *info = AllocStructSafe(struct TimerInfo);
|
|
if (info == NULL)
|
|
return NULL;
|
|
|
|
info->InterruptTimer = true;
|
|
info->InterruptTask = task;
|
|
info->InterruptSigBit = 1 << sigBit;
|
|
|
|
info->TimerPort = AllocStructSafe(struct MsgPort);
|
|
if (info->TimerPort == NULL)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
info->TimerInterrupt = AllocStructSafe(struct Interrupt);
|
|
if (info->TimerInterrupt == NULL)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
NewList(&(info->TimerPort->mp_MsgList));
|
|
info->TimerPort->mp_Node.ln_Type = NT_MSGPORT;
|
|
info->TimerPort->mp_Flags = PA_SOFTINT;
|
|
info->TimerPort->mp_SigTask = (struct Task *)info->TimerInterrupt;
|
|
|
|
info->TimerInterrupt->is_Code = TimerInterruptCode;
|
|
info->TimerInterrupt->is_Data = info;
|
|
info->TimerInterrupt->is_Node.ln_Pri = 0;
|
|
|
|
info->TimerIO = (struct timerequest *)CreateExtIO(info->TimerPort, sizeof(struct timerequest));
|
|
if (info->TimerIO == NULL)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
error = OpenDevice(
|
|
(STRPTR)TIMERNAME, UNIT_MICROHZ,
|
|
(struct IORequest *)info->TimerIO, 0);
|
|
|
|
if (error != 0)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
// Only one interrupt timer is supported
|
|
InterruptInfo = info;
|
|
|
|
return info;
|
|
}
|
|
|
|
/*
|
|
* Open a timer device with UNIT_MICROHZ.
|
|
*/
|
|
struct TimerInfo *CreateTimer(void)
|
|
{
|
|
LONG error;
|
|
static const char *name = APP_SHORT_NAME " Timer Message Port";
|
|
struct TimerInfo *info = AllocStructSafe(struct TimerInfo);
|
|
if (info == NULL)
|
|
return NULL;
|
|
|
|
info->TimerPort = CreateMsgPort();
|
|
if (info->TimerPort == NULL)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
info->TimerPort->mp_Node.ln_Name = (char *)name;
|
|
info->TimerPort->mp_Node.ln_Pri = 0;
|
|
|
|
info->TimerIO = (struct timerequest *)CreateExtIO(info->TimerPort, sizeof(struct timerequest));
|
|
if (info->TimerIO == NULL)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
error = OpenDevice(
|
|
(STRPTR)TIMERNAME, UNIT_MICROHZ,
|
|
(struct IORequest *)info->TimerIO, 0);
|
|
|
|
if (error != 0)
|
|
{
|
|
DeleteTimer(info);
|
|
return NULL;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
void DeleteTimer(struct TimerInfo *info)
|
|
{
|
|
if (info == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (info->TimerIO != NULL)
|
|
{
|
|
if (info->TimerInterrupt)
|
|
{
|
|
info->TimerFlag = TIMER_INTERRUPT_OFF;
|
|
while (info->TimerFlag != TIMER_INTERRUPT_STOPPED)
|
|
{
|
|
Delay(10);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(CheckIO((struct IORequest *)info->TimerIO)))
|
|
{
|
|
AbortIO((struct IORequest *)info->TimerIO);
|
|
}
|
|
WaitIO((struct IORequest *)info->TimerIO);
|
|
}
|
|
|
|
CloseDevice((struct IORequest *)info->TimerIO);
|
|
DeleteExtIO((struct IORequest *)info->TimerIO);
|
|
info->TimerIO = NULL;
|
|
}
|
|
|
|
if (info->TimerPort != NULL)
|
|
{
|
|
if (info->InterruptTimer)
|
|
{
|
|
FreeMemSafe(info->TimerPort);
|
|
}
|
|
else
|
|
{
|
|
DeleteMsgPort(info->TimerPort);
|
|
}
|
|
}
|
|
|
|
if (info->TimerInterrupt != NULL)
|
|
{
|
|
FreeMemSafe(info->TimerInterrupt);
|
|
}
|
|
|
|
FreeMemSafe(info);
|
|
}
|
|
|
|
static int seed = 123456789;
|
|
|
|
void SeedRandom(void)
|
|
{
|
|
struct timeval tv;
|
|
GetLocalTimeOfDay(&tv);
|
|
seed = tv.tv_micro;
|
|
}
|
|
|
|
int RandomFast(void)
|
|
{
|
|
seed = (1103515245 * seed + 12345) % 0x7fffffff;
|
|
return seed;
|
|
}
|