RCS_ID_C="$Id: amiga_time.c,v 1.15 1993/06/04 11:16:15 jraja Exp $";
/*
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
 *                    Helsinki University of Technology, Finland.
 *                    All rights reserved.
 *
 * $Log: amiga_time.c,v $
 * Revision 1.15  1993/06/04  11:16:15  jraja
 * Fixes for first public release.
 *
 * Revision 1.14  1993/05/17  01:07:47  ppessi
 * Changed RCS version.
 *
 * Revision 1.13  1993/05/14  23:14:02  ppessi
 * Updated to the latest ARP includes.
 *
 * Revision 1.12  93/04/19  02:23:18  02:23:18  ppessi (Pekka Pessi)
 * Removed arp proto file
 * 
 * Revision 1.11  93/04/06  15:15:45  15:15:45  jraja (Jarno Tapio Rajahalme)
 * Changed spl function return value storage to spl_t,
 * changed bcopys and bzeros to aligned and/or const when possible,
 * added inclusion of conf.h to every .c file.
 * 
 * Revision 1.10  93/03/15  14:41:00  14:41:00  jraja (Jarno Tapio Rajahalme)
 * Changed the initialization of timeoutRequests to explicitly copy needed 
 * structure fields from the timerIORequest. Node type is initialized to
 * NT_UNKNOWN to indicate thet request has never been used.
 * 
 * Revision 1.9  93/03/13  17:12:59  17:12:59  ppessi (Pekka Pessi)
 * Fixed bugs with variable initializations. Works with UDP.
 * 
 * Revision 1.8  93/03/10  23:08:12  23:08:12  jraja (Jarno Tapio Rajahalme)
 * Changed timer_init() to return signal mask.
 * 
 * Revision 1.7  93/03/10  22:07:07  22:07:07  jraja (Jarno Tapio Rajahalme)
 * Made timeoutRequest handling more elegant.
 * 
 * Revision 1.6  93/03/05  21:11:13  21:11:13  jraja (Jarno Tapio Rajahalme)
 * Fixed includes (again).
 * 
 * Revision 1.5  93/03/05  12:30:59  12:30:59  jraja (Jarno Tapio Rajahalme)
 * Removed #if __GNUC -stuff since TimerBase is now Library in GNUC, too.
 * 
 * Revision 1.4  93/03/05  03:26:12  03:26:12  ppessi (Pekka Pessi)
 * Compiles with SASC. Initial test version.
 * 
 * Revision 1.3  93/02/26  11:06:13  11:06:13  jraja (Jarno Tapio Rajahalme)
 * Changed amiga.lib functions (CreatePort, DeletePort, CreateExtIO and 
 * DeleteExtIO) to exec V36 equivalents (CreateMsgPort, DeleteMsgPort, 
 * CreateIORequest and DeleteIORequest).
 * 
 * Revision 1.2  93/02/24  12:53:09  12:53:09  jraja (Jarno Tapio Rajahalme)
 * Changed init to remember if initialized.
 * 
 * Revision 1.1  93/02/04  18:58:40  18:58:40  jraja (Jarno Tapio Rajahalme)
 * Initial revision
 * 
 */

#include <conf.h>

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/synch.h>
#include <sys/syslog.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>	/* for protocol timeouts */
#include <net/if.h>		/* for if timeout */
#include <netinet/in.h>
#include <net/sana2arp.h>	/* for arp timeout */

#include <kern/amiga_includes.h>
#include <kern/amiga_time.h>

/*
 * include prototypes for timeout functions
 */
#include <net/if_protos.h>              /* if_slowtimo() */
#include <kern/uipc_domain_protos.h>    /* pfslowtimo(), pffasttimo() */

/*
 * Global timer base pointer used throughout the code.
 * Commodore says that same base should not be used by different task,
 * so API users should have their own base pointer.
 */
struct Library     *TimerBase = NULL;

static struct MsgPort     *timerport = NULL;	 /* Timer message reply port */
static struct timerequest *timerIORequest = NULL; /* template IORequest */

/*
 * timeoutrequest pointers for all the timeouts
 */
static struct timeoutRequest *ifTimer = NULL,
  *arpTimer = NULL, 
  *protoSlowTimer = NULL, 
  *protoFastTimer = NULL;

static BOOL can_send_timeouts = FALSE; 

/*
 * Initialize the timer. This MUST be called before any Timer functions are
 * used (including get_time() and microtime() which use GetSysTime())
 * (see sys/time.h).
 *
 * timerIORequest is used as a template from which all needed timer
 * IO messages are copied. The command field of the request is initialized to
 * TR_ADDREQUEST. Requests node type is initialized to NT_UNKNOWN for us
 * to be able to recognize if it has been used.
 *
 * Note that we need to check manually the version of the opened device. The 
 * version number must be at least 36 since we use the GetSysTime() function
 * which is not defined in earlier versions.
 *
 * This initializes all the needed timeoutrequests too.
 */
ULONG
timer_init(void)
{
  LONG error;

  /*
   * Return success if already initialized
   */
  if (timerport != NULL)
    return TRUE;

  /*
   * allocate and initialize the timer message reply port
   */
  timerport = CreateMsgPort();
  if (timerport != NULL) {
    /*
     * allocate and initialize the template message structure
     */
    timerIORequest = (struct timerequest *)
      CreateIORequest(timerport, sizeof(struct timerequest));
    if (timerIORequest != NULL) {
      error = OpenDevice(TIMERNAME, UNIT_VBLANK, 
			 (struct IORequest *)timerIORequest, 0);
      if (error == 0) {
	/*
	 * Make sure that we got at least V36 timer, since we use some
	 * functions defined only in V36 and later.
	 */
	if ((timerIORequest->tr_node.io_Device)->dd_Library.lib_Version >= 36) {
	  /*
	   * initialize TimerBase from timerIORequest
	   */
	  TimerBase = (struct Library *)timerIORequest->tr_node.io_Device;
	  /*
	   * Initialize some fields of the IO request to common values
	   */
	  timerIORequest->tr_node.io_Command = TR_ADDREQUEST;
	  /*
	   * NT_UNKNOWN means unused, too (see note on exec/nodes.h)
	   */
	  timerIORequest->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;

	  /*
	   * create timeout requests for all timeouts;
	   */
	  ifTimer = createTimeoutRequest(if_slowtimo, 1 / IFNET_SLOWHZ, 0); 
	  arpTimer = createTimeoutRequest(arptimer, ARPT_AGE, 0); 
	  protoSlowTimer = createTimeoutRequest(pfslowtimo, 0, 1000000 / PR_SLOWHZ); 
	  protoFastTimer = createTimeoutRequest(pffasttimo, 0, 1000000 / PR_FASTHZ); 
	  if (protoFastTimer && protoSlowTimer && arpTimer && ifTimer) {
	    can_send_timeouts = TRUE;
	    return (ULONG)(1 << timerport->mp_SigBit);
	  }
	}
      }
    }
  }
  /*
   * clean all in case of any error
   */
  timer_deinit();

  return (0);
}

/*
 * Deinitialize the timer.
 * The requests are cancelled first. 
 */
void
timer_deinit(void)
{
  can_send_timeouts = FALSE;

  if (protoFastTimer)
    deleteTimeoutRequest(protoFastTimer);
  if (protoSlowTimer)
    deleteTimeoutRequest(protoSlowTimer);
  if (arpTimer)
    deleteTimeoutRequest(arpTimer);
  if (ifTimer)
    deleteTimeoutRequest(ifTimer);

  if (timerIORequest) {
    TimerBase = NULL;
    if (timerIORequest->tr_node.io_Device != NULL)
      CloseDevice((struct IORequest *)timerIORequest);
    DeleteIORequest((struct IORequest *)timerIORequest);
    timerIORequest = NULL;
  }
  if (timerport) {
    DeleteMsgPort(timerport);
    timerport = NULL;
  }
}

/*
 * Function to send all the timeout requests when everything is initialized
 * DON'T even try to call this function otherwise!
 */
void
timer_send(void)
{
  if (can_send_timeouts != FALSE) {
    /*
     * Start timeout requests
     */
    sendTimeoutRequest(ifTimer);
    sendTimeoutRequest(arpTimer);
    sendTimeoutRequest(protoSlowTimer);
    sendTimeoutRequest(protoFastTimer);

    can_send_timeouts = FALSE;
  }
}

/*
 * functions to create new timeoutRequest (after timer is initialized!)
 */
struct timeoutRequest *
createTimeoutRequest(TimerCallback_t fun, 
		     ULONG seconds, ULONG micros)
{
  struct timeoutRequest *tr;
#if DIAGNOSTIC
  /*
   * sanity check the micros value
   */
  if (micros >= 1000000) {
    log(LOG_ERR, "More than 1000000 microseconds in initTimeoutRequest()\n");
    return NULL;
  }
#endif

  /*
   * allocate IO request
   */
  tr = CreateIORequest(timerport, sizeof(*tr));
  if (tr == NULL)
    return NULL;

  /*
   * copy initial values from the initialized timerrequest
   */

  /* Node */
  tr->timeout_request.tr_node.io_Message.mn_Node.ln_Type = 
    timerIORequest->tr_node.io_Message.mn_Node.ln_Type;
  tr->timeout_request.tr_node.io_Message.mn_Node.ln_Pri = 
    timerIORequest->tr_node.io_Message.mn_Node.ln_Pri;
  tr->timeout_request.tr_node.io_Message.mn_Node.ln_Name = 
    timerIORequest->tr_node.io_Message.mn_Node.ln_Name;

  /* Message */
  tr->timeout_request.tr_node.io_Message.mn_ReplyPort = 
    timerIORequest->tr_node.io_Message.mn_ReplyPort;

  /* IORequest */
  tr->timeout_request.tr_node.io_Device = timerIORequest->tr_node.io_Device;
  tr->timeout_request.tr_node.io_Unit = timerIORequest->tr_node.io_Unit;
  tr->timeout_request.tr_node.io_Command = timerIORequest->tr_node.io_Command;
  tr->timeout_request.tr_node.io_Flags = timerIORequest->tr_node.io_Flags;

  /*
   * set our own fields
   */
  tr->timeout_timeval.tv_secs = seconds;
  tr->timeout_timeval.tv_micro = micros;
  tr->timeout_function = fun;

  return tr;
}

void 
deleteTimeoutRequest(struct timeoutRequest *tr)
{
  /*
   * Abort the request if ever used
   */
  if (((struct Node *)tr)->ln_Type != NT_UNKNOWN) {
    AbortIO((struct IORequest *)tr);
    WaitIO((struct IORequest *)tr);
    /*
     * Make sure the signal gets cleared
     */
    SetSignal(0, 1 << timerport->mp_SigBit);
  }
  /*
   * free the request
   */
  DeleteIORequest(tr);
}

BOOL timer_poll(VOID)
{
  struct timeoutRequest *timerReply;

  /*
   * Get all messages from the timer reply port.
   */
  if (timerReply = (struct timeoutRequest *)GetMsg(timerport)) {
    /*
     * enter softclock interrupt level
     */
    spl_t s = splsoftclock();
    /*
     * handle the timeout
     */
    handleTimeoutRequest(timerReply);
    /*
     * restart timeout request
     */
    sendTimeoutRequest(timerReply);
    /*
     * restore previous interrupt level
     */
    splx(s);

    return TRUE;
  }

  return FALSE;
}
