#ifndef __QNXNTO__

/* Stolen from existing qnx4 audio, useful for implementing
 * coprocessing.  
 */

#include "driver.h"
#include "qnx4_proc.h"

#define MAXPRIO 2
int WCHAN = 0;

extern void near *_ncalloc();

/* access a specific register */
#define PREG(proc, reg)		((struct jmpreg *)(proc->p_regs))->reg

struct jmpreg {
#ifndef __386__
    short   bx, cx, dx, si, di, bp, sp;
    short   es, ds, cs;
    short   ip, psw;
    short   ss;
#else
    long    bx, cx, dx, si, di, bp, ip, sp;
    short   es, ds, cs, fs, gs, ss;
#endif
};

/*
 * some available data:
 *    curproc	-- the currently active entry.
 *    nprocs	-- current number of entries.
 *    lbolt	-- updated each timer tick.
 */
kent_t	curproc;
int	nprocs;
int	lbolt;


#if MAXPRIO > 1
/*
 * range of priorities to worry about
 */
static int priolo = 0, priohi = MAXPRIO-1;
#endif


static	kent_t near rdyq[MAXPRIO][2];
static	kent_t near slpq[2];
	kent_t allproc;
	int    maxproc;
static	int    stklen;

#define ch_state(p, s)	((p)->p_stat = (s))

#define ctx_save(p) setjmp((p)->p_regs)
#define ctx_load(p)	longjmp((p)->p_regs, 1)

/* just to clean up source a bit */
#define head(q)		((q)[0])
#define tail(q)		((q)[1])
#define next(p)		((p)->p_nxt)

static
kent_t near deq(kent_t near *q) {
    kent_t k;

    if (k = head(q)) {
	if ((head(q) = next(k)) == 0)
	    tail(q) = 0;
    } else
	panic("deq: no entry\n");
    next(k) = 0;
    return k;
}

static
void near enq(kent_t near *q, kent_t k)
{
    if (tail(q))
		next(tail(q)) = k;
    else
		head(q) = k;

    tail(q) = k;
}

static
void near mkready(kent_t p)
{
    p->p_wchan = 0;
    ch_state(p, READY);
    next(p) = 0;
#if MAXPRIO > 1
    priolo = min(priolo, p->p_pri);
    priohi = max(priohi, p->p_pri);
    enq(rdyq[p->p_pri], p);
#else
    enq(rdyq, p);
#endif
}

static
int near apply_q(kent_t near *q, int near (*f)(kent_t, caddr_t), caddr_t arg)
{
    kent_t prev, cur;
    int cnt = 0;

    for (prev = 0, cur = head(q); cur ;) {
		if ((*f)(cur, arg)) {
		    kent_t t = next(cur);

		    cnt++;
		    if (prev) {
				if ((next(prev) = t) == 0) 	tail(q) = prev;
		    } else
				if ((head(q) = t) == 0) 	tail(q) = 0;
 
		    mkready(cur);
		    cur = t;
		} else {
		    prev = cur, cur = next(cur);
		}
    }
    return cnt;
}

void near sched() {
    kent_t p = curproc;

    if (p && p->p_stat == READY) {
#if MAXPRIO > 1
		if (p->p_pri > priohi || (p->p_pri == priohi && !head(rdyq[p->p_pri])))
		    return;
		enq(rdyq[p->p_pri], p);
#else
		enq(rdyq, p);
#endif
    }
    for (;;) {
		kent_t t;
#if MAXPRIO > 1
		int i;

		for (i = priohi; i >= priolo && head(rdyq[i]) == 0; i--)
		    ;
		if (i < priolo)
		    panic("nobody to schedule!\n");
		t = deq(rdyq[i]);
#else
		t = deq(rdyq);
#endif
#ifdef DIAGNOSTIC
		{
		    extern _STACKLOW, _STACKTOP;
		    _STACKLOW = (int) t->p_stkbase;
		    _STACKTOP = (int) t->p_stkbase + stklen;
		}
#endif
#if 0
		printf("nprocs = %i, curproc = %i \n", nprocs, t->p_thread);

		if (t->p_thread >= nprocs)
			printf("	Thread is invalid (index is too large)!!!\n");
		else
#endif
		ctx_load(curproc = t);
    }
}

int near resched()
{
    if (curproc) {
		curproc->p_rval = EOK;
		if (ctx_save(curproc)) {
		    return curproc->p_rval;
		}
   	}
    sched();
    return curproc->p_rval;
}

int goto_sleep(caddr_t wchan, int prio)
{

    curproc->p_wchan = wchan;
#if MAXPRIO > 1
    curproc->p_pri = prio;
#endif
    ch_state(curproc, ASLEEP);
    enq(slpq, curproc);

    return resched();
}

static
int fndchan(kent_t k, caddr_t wchan) {
    return k->p_wchan == wchan;
}

static
int fndproc(kent_t k, caddr_t x) {
    return k == (kent_t) x;
}

int wakeup(caddr_t wchan)
{
    return apply_q(slpq, fndchan, (caddr_t) wchan) ? 0 : -1;
}

int unsleep(kent_t p)
{
    if (apply_q(slpq, fndproc, (caddr_t) p)) {
		p->p_rval = EWOULDBLOCK;
		return 0;
    }
    return -1;
}

static
void freek(kent_t p) {
    ch_state(p, EMPTY);
    nprocs--;
}

static
kent_t allock() {
    kent_t p;

    for (p = allproc; p < allproc + maxproc; p++) {
	if (p->p_stat == EMPTY) {
	    ch_state(p, BUSY);
	    p->p_thread = p - allproc;
	    nprocs++;
	    return p;
	}
   }
   return 0;
}

void setp(int prio) {
#if MAXPRIO > 1
    curproc->p_pri = prio;

    /* adjust these values */
    for (priohi = MAXPRIO-1; priohi > 0 && head(rdyq[priohi]) == 0; priohi--)
	;
    for (priolo = 0; priolo < MAXPRIO-1  && head(rdyq[priolo]) == 0; priolo++)
	;
    resched();
#endif
}

kent_t
pfind(pid_t pid) {
    kent_t p;

    for (p = allproc; p < allproc + maxproc; p++)
	if (p->p_pid == pid)
	    return p;
   return 0;
}

kconf(int nproc, int depth) {
    kent_t initp;

    maxproc = nproc, stklen = depth;
    if ((allproc = _ncalloc(nproc, sizeof *allproc)) == 0)
	return maxproc = 0, -1;
    while (nproc--)
	if ((allproc[nproc].p_stkbase = malloc(depth)) == 0)
	    return maxproc = 0, -1;

    /* table now initialized */
    initp = allock();
#if MAXPRIO > 1
    priohi =
    initp->p_pri = 0;
#endif
    if (ctx_save(initp) == 0) {  /* we just created it */
		ch_state(curproc = initp, READY);
		initp->p_rval = EOK;
		ctx_load(initp);
    }
    return initp->p_rval;
}

static
die() {
    freek(curproc);
    curproc = 0;
    sched();
}

pcreat(int (*func)(), int prio, pid_t pid, caddr_t arg) {
    kent_t p = allock();
    u_int sp;

    if (p == 0)	return errno = EAGAIN, -1;

    sp = FP_OFF(p->p_stkbase) + stklen /* - 2 */;

    ctx_save(p);

#if 0
printf("\t\tnprocs = %i, curproc = %i \n", nprocs, p->p_thread);
#endif

#ifdef __WATCOMC__
    sp -= sizeof(void (*)());
    *(void **)sp = die;

    PREG(p, sp) = sp;
    PREG(p, ss) = FP_SEG(&sp);
    PREG(p, ip) = FP_OFF(func);
    PREG(p, cs) = FP_SEG(func);

    PREG(p, dx) = pid;
    PREG(p, bx) = FP_OFF(arg);
    PREG(p, cx) = FP_SEG(arg);
#endif

    /*
     * Initialize the remainder of the child's process environment.
     * When the child has been set up, place on appropriate
     * ready queue.  The child will start executing at the
     * desired function, with a return address of 'die'.
     */
    p->p_rval = 0;
    p->p_pid = pid;
#if MAXPRIO > 1
    p->p_pri = prio == -1 ? curproc->p_pri : prio;
#endif
    mkready(p);

    return p->p_thread;
}

void
panic(char *msg) {
    write(2, msg, strlen(msg));
}

/* end Rennie crap---start Tash crap */

#define PROXY_TABLE_SIZE 10

static struct {
        pid_t           proxy;
        int             (*func)(void *);
        void            *data;
} proxy_table[PROXY_TABLE_SIZE];


int 
proxy_callback(void *arg)
{
        caddr_t wchan = (caddr_t) arg;
        wakeup(wchan);
        return (0);
}


pid_t
register_proxy(int (*func)(void *), void *data)
{
        int             i;
        pid_t           p;

        if ((p = qnx_proxy_attach(0, NULL, 0, 20)) < 0) {
                perror("qnx_proxy_attach");
                return (-1);
	      }

        for (i = 0; i < PROXY_TABLE_SIZE; i++) {
                if (proxy_table[i].proxy == 0)
                        break;
	      }
        if (i == PROXY_TABLE_SIZE)
                return (-1);

        proxy_table[i].proxy = p;
        proxy_table[i].func = func;
        proxy_table[i].data = data;

        return (p);
}


int
put_to_sleep(sleep_entity_t *sl, int sec)
{
        struct itimerspec       ts;
        struct sigevent         ev;

        if (!sl->proxy) {

                if ((sl->proxy = register_proxy(sl->func,
                                                (void *) &sl->wchan)) < 0)
                        return (-1);
	      }

        ev.sigev_signo = -sl->proxy;

        if(!sl->tid) {
                if((sl->tid = timer_create(CLOCK_REALTIME, &ev)) < 0) {
                        perror("timer_create");
                        return (-1);
		      }
	      }
	sl->timedout = ETIMEDOUT;	// This is cleared by a valid wakeup

        memset(&ts, 0, sizeof(ts));
        ts.it_value.tv_sec = sec;
        if (timer_settime(sl->tid, 0, &ts, NULL) < 0) {
                perror("timer_settime");
                return (-1);
	}

	memset(&ts, 0, sizeof(ts));
        goto_sleep(&sl->wchan, 1);
	timer_settime(sl->tid, 0, &ts, NULL);

        return (0);
}

int
wake_up_sleeper(sleep_entity_t *sl)
{
        struct itimerspec ts;

        memset(&ts, 0, sizeof(ts));
        if (!sl->tid)
                return (-1);

	sl->timedout = 0;	// clear it b/c this is a valid wakeup, not a timeout

        timer_settime(sl->tid, 0, &ts, NULL);

        if (!sl->proxy)
                return (-1);

        Trigger(sl->proxy);
        return (0);
}

int
chk_pid(pid_t pid)
{
        int i;

        for (i = 0; i < PROXY_TABLE_SIZE; i++) {
                if (proxy_table[i].proxy == pid)
                        break;
        }
        if (i == PROXY_TABLE_SIZE)
                return (0);
        else
                proxy_table[i].func(proxy_table[i].data);

        return (1);
}


#endif
