#include <linux/sched.h>
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/interrupt.h>
 #include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <asm/uaccess.h>
+#include "irq_kern.h"
 #include "os.h"
 
 /*
  * core module and version information
  */
 #define RNG_VERSION "1.0.0"
-#define RNG_MODULE_NAME "random"
+#define RNG_MODULE_NAME "hw_random"
 
 #define RNG_MISCDEV_MINOR              183 /* official */
 
  * protects against a module being loaded twice at the same time.
  */
 static int random_fd = -1;
+static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);
 
 static int rng_dev_open (struct inode *inode, struct file *filp)
 {
        return 0;
 }
 
+static atomic_t host_sleep_count = ATOMIC_INIT(0);
+
 static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
                              loff_t * offp)
 {
                         }
                 }
                 else if(n == -EAGAIN){
+                       DECLARE_WAITQUEUE(wait, current);
+
                         if (filp->f_flags & O_NONBLOCK)
                                 return ret ? : -EAGAIN;
 
-                        if(need_resched())
-                                schedule_timeout_interruptible(1);
+                       atomic_inc(&host_sleep_count);
+                       reactivate_fd(random_fd, RANDOM_IRQ);
+                       add_sigio_fd(random_fd);
+
+                       add_wait_queue(&host_read_wait, &wait);
+                       set_task_state(current, TASK_INTERRUPTIBLE);
+
+                       schedule();
+                       set_task_state(current, TASK_RUNNING);
+                       remove_wait_queue(&host_read_wait, &wait);
+
+                       if (atomic_dec_and_test(&host_sleep_count)) {
+                               ignore_sigio_fd(random_fd);
+                               deactivate_fd(random_fd, RANDOM_IRQ);
+                       }
                 }
                 else return n;
                if (signal_pending (current))
        &rng_chrdev_ops,
 };
 
+static irqreturn_t random_interrupt(int irq, void *data)
+{
+       wake_up(&host_read_wait);
+
+       return IRQ_HANDLED;
+}
+
 /*
  * rng_init - initialize RNG module
  */
 
         random_fd = err;
 
-        err = os_set_fd_block(random_fd, 0);
+       err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
+                            IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "random",
+                            NULL);
         if(err)
                goto err_out_cleanup_hw;
 
+       sigio_broken(random_fd, 1);
+
        err = misc_register (&rng_miscdev);
        if (err) {
                printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n");
         return err;
 
  err_out_cleanup_hw:
+       os_close_file(random_fd);
         random_fd = -1;
         goto out;
 }
  */
 static void __exit rng_cleanup (void)
 {
+       os_close_file(random_fd);
        misc_deregister (&rng_miscdev);
 }
 
 
 extern int add_sigio_fd(int fd);
 extern int ignore_sigio_fd(int fd);
 extern void maybe_sigio_broken(int fd, int read);
+extern void sigio_broken(int fd, int read);
 
 /* sys-x86_64/prctl.c */
 extern int os_arch_prctl(int pid, int code, unsigned long *addr);
 
 /* 
- * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Copyright (C) 2000 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
  * Licensed under the GPL
  */
 
 
 #include <signal.h>
 
+/* Copied from linux/compiler-gcc.h since we can't include it directly */
+#define barrier() __asm__ __volatile__("": : :"memory")
+
 extern void sig_handler(int sig, struct sigcontext sc);
 extern void alarm_handler(int sig, struct sigcontext sc);
 
 #endif
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
 
 /*
- * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
  * Licensed under the GPL
  */
 
 #include "kern_util.h"
 #include "init.h"
 #include "os.h"
+#include "process.h"
 #include "sigio.h"
 #include "um_malloc.h"
 #include "user.h"
        close(l_write_sigio_fds[1]);
 }
 
-/* Changed during early boot */
-static int pty_output_sigio = 0;
-static int pty_close_sigio = 0;
-
-void maybe_sigio_broken(int fd, int read)
+void sigio_broken(int fd, int read)
 {
        int err;
 
-       if (!isatty(fd))
-               return;
-
-       if ((read || pty_output_sigio) && (!read || pty_close_sigio))
-               return;
-
        write_sigio_workaround();
 
        sigio_lock();
        sigio_unlock();
 }
 
+/* Changed during early boot */
+static int pty_output_sigio;
+static int pty_close_sigio;
+
+void maybe_sigio_broken(int fd, int read)
+{
+       if (!isatty(fd))
+               return;
+
+       if ((read || pty_output_sigio) && (!read || pty_close_sigio))
+               return;
+
+       sigio_broken(fd, read);
+}
+
 static void sigio_cleanup(void)
 {
        if (write_sigio_pid == -1)
 __uml_exitcall(sigio_cleanup);
 
 /* Used as a flag during SIGIO testing early in boot */
-static volatile int got_sigio = 0;
+static int got_sigio;
 
 static void __init handler(int sig)
 {
        if (errno != EAGAIN)
                printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n",
                       errno);
-       while (((n = read(slave, buf, sizeof(buf))) > 0) && !got_sigio)
+       while (((n = read(slave, buf, sizeof(buf))) > 0) &&
+              !({ barrier(); got_sigio; }))
                ;
 
        if (got_sigio) {
 
 #define SIGIO_WRITE_IRQ        11
 #define TELNETD_IRQ            12
 #define XTERM_IRQ              13
+#define RANDOM_IRQ             14
 
-#define LAST_IRQ XTERM_IRQ
+#define LAST_IRQ RANDOM_IRQ
 #define NR_IRQS (LAST_IRQ + 1)
 
 #endif