/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *  Portions Copyright (C) 1994 Rafael W. Luebbert
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  $Id: ix_sleep.c,v 1.4 1994/06/19 15:13:19 rluebbert Exp $
 *
 *  $Log: ix_sleep.c,v $
 *  Revision 1.4  1994/06/19  15:13:19  rluebbert
 *  *** empty log message ***
 *
 * 
 * 
 */

#define _KERNEL
#include "ixemul.h"
#include "kprintf.h"

#define __time_req (u.u_time_req)
#define __tport    (u.u_sync_mp)

/* this is the `message' we queue on the sleep queues. */
struct sleep_msg {
  struct MinNode 	sm_node;
  short			sm_signal;
  struct Task*		sm_sigtask;
  u_int			sm_waitchan;
};


static inline u_short
ix_hash (u_int waitchan)
{
  unsigned short res;

  res = (waitchan >> 16) ^ (waitchan & 0xffff);
  res %= IX_NUM_SLEEP_QUEUES;
  return res; 
}

int
tsleep(caddr_t waitchan, char *wmesg, int timo)
{
  /* we run in the context of the calling task, we generate a sleep msg and
   * add it to the right sleep queue. wakeup() will do the rest.
   */
  struct sleep_msg sm;
  struct MinList *the_list;
  u_int wait_sigs;
  int res = -1;

  if (CURSIG (& u))
    {
      errno = EINTR;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return -1;
    }

  sm.sm_sigtask = FindTask (0);
  sm.sm_waitchan = (u_int)waitchan;

  u.p_stat = SSLEEP;	/* so that machdep.c can interrupt us */
  u.p_wchan = (caddr_t) waitchan;
  u.p_wmesg = wmesg;
  the_list = &ixemulbase->ix_sleep_queues[ix_hash((u_int)waitchan)];
  
  sm.sm_signal = u.u_sleep_sig;

  wait_sigs =  (1 << sm.sm_signal) | SIGBREAKF_CTRL_C;
  
  if (timo)
    {
      __time_req->tr_time.tv_sec = timo % 60;
      __time_req->tr_time.tv_usec = timo / 60;
      __time_req->tr_node.io_Command = TR_ADDREQUEST;
      SetSignal (0, 1 << __tport->mp_SigBit);
      SendIO((struct IORequest *)__time_req);
      wait_sigs |= 1 << __tport->mp_SigBit;
    }

  Forbid();
  AddTail ((struct List *) the_list, (struct Node *) &sm);

  /* this will break the Disable () and reestablish it afterwards */
  res = Wait (wait_sigs);
  /* this conversion is inhibited in the Launch handler as long as we're
     in SSLEEP state. Since the SetSignal() below will remove all traces
     of a perhaps present SIGBREAKF_CTRL_C, we'll have to do the conversion
     here ourselves */
  if (((u.p_sigignore & sigmask(SIGMSG)) || !(u.p_sigcatch & sigmask(SIGMSG)))
      && (res & SIGBREAKF_CTRL_C))
    _psignalgrp((struct Process *)FindTask(0), SIGINT);
  SetSignal (0, res);
  res = CURSIG (&u) ? -1 : 0;

  Remove ((struct Node *) &sm);
  Permit();

  if (timo)
    {
      if (! CheckIO ((struct IORequest *)__time_req))
        AbortIO ((struct IORequest *)__time_req);
      WaitIO ((struct IORequest *)__time_req);
    }

  u.p_wchan = 0;
  u.p_wmesg = 0;
  u.p_stat = SRUN;
  if (res)
    errno = EINTR;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return res;
}

int
ix_sleep (caddr_t chan, char *wmesg)
{
  return tsleep (chan, wmesg, 0);
}


/*
 * ix_wakeup() can be called from an interrupt (and is called that way;-))
 */

void
ix_wakeup (u_int waitchan)
{
  struct MinList *the_list = & ixemulbase->ix_sleep_queues[ix_hash (waitchan)];
  struct sleep_msg *sm, *nsm;
  
  Forbid();

  for (sm  = (struct sleep_msg *)the_list->mlh_Head;
       (nsm = (struct sleep_msg *)sm->sm_node.mln_Succ);
       sm  = nsm)
    if (sm->sm_waitchan == waitchan)
      Signal (sm->sm_sigtask, 1 << sm->sm_signal);

  Permit();
}
