/*
    (C) 1995-96 AROS - The Amiga Replacement OS
    $Id$

    Desc: Timer.device
    Lang: english
*/
#define timeval     linux_timeval
#include <sys/time.h>
#undef timeval
#include <devices/timer.h>
#include <clib/exec_protos.h>
#include <aros/libcall.h>
#ifdef __GNUC__
#   include "nil_handler_gcc.h"
#endif

static const char name[];
static const char version[];
static const APTR inittabl[4];
static void *const functable[];
struct nilbase *AROS_SLIB_ENTRY(init,timer)();
void AROS_SLIB_ENTRY(open,timer)();
BPTR AROS_SLIB_ENTRY(close,timer)();
BPTR AROS_SLIB_ENTRY(expunge,timer)();
int AROS_SLIB_ENTRY(null,timer)();
void AROS_SLIB_ENTRY(beginio,timer)();
LONG AROS_SLIB_ENTRY(abortio,timer)();
static const char end;
static void NextTick (ULONG usec);
static void timer (int dummy);

int timer_entry(void)
{
    /* If the handler was executed by accident return error code. */
    return -1;
}

const struct Resident timer_resident=
{
    RTC_MATCHWORD,
    (struct Resident *)&timer_resident,
    (APTR)&end,
    RTF_AUTOINIT,
    1,
    NT_DEVICE,
    0,
    (char *)name,
    (char *)&version[6],
    (ULONG *)inittabl
};

static const char name[]=TIMERNAME;

static const char version[]="$VER: " TIMERNAME " 1.0 (17.10.96)\n\015";

static const APTR inittabl[4]=
{
    (APTR)sizeof(struct timerbase),
    (APTR)functable,
    NULL,
    &AROS_SLIB_ENTRY(init,timer)
};

static void *const functable[]=
{
    &AROS_SLIB_ENTRY(open,timer),
    &AROS_SLIB_ENTRY(close,timer),
    &AROS_SLIB_ENTRY(expunge,timer),
    &AROS_SLIB_ENTRY(null,timer),
    &AROS_SLIB_ENTRY(beginio,timer),
    &AROS_SLIB_ENTRY(abortio,timer),
    (void *)-1
};

AROS_LH2(struct timerbase *, init,
 AROS_LHA(struct timerbase *, timerbase, D0),
 AROS_LHA(BPTR,             segList, A0),
	   struct ExecBase *, sysBase, 0, timer)
{
    AROS_LIBFUNC_INIT
    struct itimerval interval;

    /* Store arguments */
    timerbase->sysbase=sysBase;
    timerbase->seglist=segList;

    /* Start timer */
    signal (SIGALRM, timer);

    interval.it_interval.tv_sec = interval.it_value.tv_sec = 0;
    interval.it_interval.tv_usec = interval.it_value.tv_usec = 1000000/50;

    setitimer (ITIMER_REAL, &interval, NULL);

    return NULL;
    AROS_LIBFUNC_EXIT
}

AROS_LH3(void, open,
 AROS_LHA(struct IOFileSys *, iofs, A1),
 AROS_LHA(ULONG,              unitnum, D0),
 AROS_LHA(ULONG,              flags, D0),
	   struct timerbase *, timerbase, 1, timer)
{
    AROS_LIBFUNC_INIT
    struct TimeRequest *unit;

    /* Get compiler happy */
    unitnum=flags=0;

    /* I have one more opener. */
    timerbase->device.dd_Library.lib_OpenCnt++;

    /* Mark Message as recently used. */
    iofs->IOFS.io_Message.mn_Node.ln_Type=NT_REPLYMSG;

    unit=AllocMem(sizeof(struct TimeRequest),MEMF_PUBLIC|MEMF_CLEAR);
    if(dev!=NULL)
    {
	iofs->IOFS.io_Unit=(struct Unit *)unit;
	iofs->IOFS.io_Device=&timerbase->device;
	timerbase->device.dd_Library.lib_Flags&=~LIBF_DELEXP;
	iofs->IOFS.io_Error=0;
	return;
    }else
	iofs->io_DosError=ERROR_NO_FREE_STORE;

    iofs->IOFS.io_Error=IOERR_OPENFAIL;
    timerbase->device.dd_Library.lib_OpenCnt--;

    AROS_LIBFUNC_EXIT
}

AROS_LH1(BPTR, close,
 AROS_LHA(struct IOFileSys *, iofs, A1),
	   struct timerbase *, timerbase, 2, timer)
{
    AROS_LIBFUNC_INIT
    struct TimeRequest *unit;

    unit=(struct TimeRequest *)iofs->IOFS.io_Unit;
    if(unit->count)
    {
	iofs->io_DosError=ERROR_OBJECT_IN_USE;
	return 0;
    }

    /* Let any following attemps to use the device crash hard. */
    iofs->IOFS.io_Device=(struct Device *)-1;
    FreeMem(unit,sizeof(struct TimeRequest));
    iofs->io_DosError=0;

    /* I have one fewer opener. */
    timerbase->device.dd_Library.lib_OpenCnt --;

    return 0;
    AROS_LIBFUNC_EXIT
}

AROS_LH0(BPTR, expunge, struct timerbase *, timerbase, 3, timer)
{
    AROS_LIBFUNC_INIT
    return 0; /* Never allow to expunge */
    AROS_LIBFUNC_EXIT
}

AROS_LH0I(int, null, struct timerbase *, timerbase, 4, timer)
{
    AROS_LIBFUNC_INIT
    return 0;
    AROS_LIBFUNC_EXIT
}

AROS_LH1(void, beginio,
 AROS_LHA(struct timerequest *, tr, A1),
	   struct timerbase *, timerbase, 5, timer)
{
    AROS_LIBFUNC_INIT
    LONG error=0;

    /*
	Do everything quick no matter what. This is possible
	because I never need to Wait().
    */
    switch(tr->tr_node.io_Command)
    {
    case TR_ADDREQUEST:

    default:
	error=ERROR_NOT_IMPLEMENTED;
	break;
    }

    /* Set error code */
    tr->tr_node.io_Error=error;

    /* If the quick bit is not set send the message to the port */
    if(!(tr->tr_node.io_Flags&IOF_QUICK))
	ReplyMsg(&tr->tr_node.io_Message);

    AROS_LIBFUNC_EXIT
}

AROS_LH1(LONG, abortio,
 AROS_LHA(struct IOFileSys *, iofs, A1),
	   struct timerbase *, timerbase, 6, timer)
{
    AROS_LIBFUNC_INIT
    /* Everything already done. */
    return 0;
    AROS_LIBFUNC_EXIT
}

static void timer (int dummy)
{
    signal (SIGALRM, timer);

    if (SysBase->TDNestCnt >= 0
	&& SysBase->ThisTask->tc_Node.ln_Pri <=
	    ((struct Task *)SysBase->TaskReady.lh_Head)->tc_Node.ln_Pri)
	Switch ();
    else
	SysBase->AttnResched |= 0x80;
}

static const char end=0;
