/*- * Copyright (c) 2017-2021 Carsten Sonne Larsen * 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 #include #include #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; }