#include        "u.h"
#include        "../port/lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "../port/error.h"


struct Rb
{
       QLock;
       Rendez  producer;
       Rendez  consumer;
       ulong   randomcount;
       uchar   buf[1024];
       uchar   *ep;
       uchar   *rp;
       uchar   *wp;
       uchar   next;
       uchar   wakeme;
       ushort  bits;
       ulong   randn;
} rb;

static int
rbnotfull(void*)
{
       int i;

       i = rb.rp - rb.wp;
       return i != 1 && i != (1 - sizeof(rb.buf));
}

static int
rbnotempty(void*)
{
       return rb.wp != rb.rp;
}

void
genrandom(void*)
{
       up->basepri = PriNormal;
       up->priority = up->basepri;

       for(;;){
               for(;;)
                       if(++rb.randomcount > 100000)
                               break;
               if(anyhigher())
                       sched();
               if(!rbnotfull(0))
                       sleep(&rb.producer, rbnotfull, 0);
       }
}

/*
*  produce random bits in a circular buffer
*/
static void
randomclock(void)
{
       if(rb.randomcount == 0 || !rbnotfull(0))
               return;

       rb.bits = (rb.bits<<2) ^ rb.randomcount;
       rb.randomcount = 0;

       rb.next++;
       if(rb.next != 8/2)
               return;
       rb.next = 0;

       *rb.wp ^= rb.bits;
       if(rb.wp+1 == rb.ep)
               rb.wp = rb.buf;
       else
               rb.wp = rb.wp+1;

       if(rb.wakeme)
               wakeup(&rb.consumer);
}

void
randominit(void)
{
       addclock0link(randomclock);
       rb.ep = rb.buf + sizeof(rb.buf);
       rb.rp = rb.wp = rb.buf;
       kproc("genrandom", genrandom, 0);
}

/*
*  consume random bytes from a circular buffer
*/
ulong
randomread(void *xp, ulong n)
{
       uchar *e, *p;
       ulong x;

       p = xp;

       if(waserror()){
               qunlock(&rb);
               nexterror();
       }

       qlock(&rb);
       for(e = p + n; p < e; ){
               if(rb.wp == rb.rp){
                       rb.wakeme = 1;
                       wakeup(&rb.producer);
                       sleep(&rb.consumer, rbnotempty, 0);
                       rb.wakeme = 0;
                       continue;
               }

               /*
                *  beating clocks will be precictable if
                *  they are synchronized.  Use a cheap pseudo
                *  random number generator to obscure any cycles.
                */
               x = rb.randn*1103515245 ^ *rb.rp;
               *p++ = rb.randn = x;

               if(rb.rp+1 == rb.ep)
                       rb.rp = rb.buf;
               else
                       rb.rp = rb.rp+1;
       }
       qunlock(&rb);
       poperror();

       wakeup(&rb.producer);

       return n;
}