#define KERNEL
#include "ixemul.h"

#ifdef DEBUG
#define DP(a) kprintf a
#else
#define DP(a)
#endif

extern struct ixemul_base *ixemulbase;

/* 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; 
}

void
ix_sleep (u_int waitchan)
{
  /* 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, omask;
  
  sm.sm_sigtask = FindTask (0);
  sm.sm_waitchan = waitchan;

  the_list = & ixemulbase->ix_sleep_queues [ix_hash (waitchan)];
  
  omask = syscall (SYS_sigsetmask, ~0);

  sm.sm_signal  = AllocSignal (-1);
  /* in that case we become a poller, but there's nothing I can do about it.. */
  if (sm.sm_signal == -1)
    goto ret;

  wait_sigs = (1 << u.u_sleep_sig) | (1<< sm.sm_signal) | SIGBREAKF_CTRL_C;

  Disable ();
  u.p_wchan = (caddr_t) waitchan;
  AddTail ((struct List *) the_list, (struct Node *) &sm);

DP(("ix_sleep: $%lx ", waitchan));
  /* this will break the Disable () and reestablish it afterwards */
  Wait (wait_sigs);
DP(("ix_sleep: back.\n"));

  Remove ((struct Node *) &sm);
  u.p_wchan = 0;
  Enable ();

  FreeSignal (sm.sm_signal);

ret:
  syscall (SYS_sigsetmask, omask);
}

/*
 * 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;
  
  Disable ();

  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);

  Enable ();
}

