diff --git a/context/context.rs b/context/context.rs
index 504b5e4666378715a4e2aeed67b795f4c4ead548..a2ab64b9ff556fe887d8d32a2b1d768edc8d33be 100644
--- a/context/context.rs
+++ b/context/context.rs
@@ -1,9 +1,10 @@
 use alloc::arc::Arc;
 use alloc::boxed::Box;
-use collections::{BTreeMap, Vec};
+use collections::{BTreeMap, Vec, VecDeque};
 use spin::Mutex;
 
 use arch;
+use syscall::data::Event;
 use super::file::File;
 use super::memory::{Grant, Memory, SharedMemory};
 
@@ -39,6 +40,8 @@ pub struct Context {
     pub grants: Arc<Mutex<Vec<Grant>>>,
     /// The current working directory
     pub cwd: Arc<Mutex<Vec<u8>>>,
+    /// Kernel events
+    pub events: Arc<Mutex<VecDeque<Event>>>,
     /// The process environment
     pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
     /// The open files in the scheme
@@ -60,6 +63,7 @@ impl Context {
             stack: None,
             grants: Arc::new(Mutex::new(Vec::new())),
             cwd: Arc::new(Mutex::new(Vec::new())),
+            events: Arc::new(Mutex::new(VecDeque::new())),
             env: Arc::new(Mutex::new(BTreeMap::new())),
             files: Arc::new(Mutex::new(Vec::new()))
         }
diff --git a/context/event.rs b/context/event.rs
new file mode 100644
index 0000000000000000000000000000000000000000..06fea60e4fa6ee7a246c7f0d80aac2f080834ae1
--- /dev/null
+++ b/context/event.rs
@@ -0,0 +1,80 @@
+use alloc::arc::{Arc, Weak};
+use collections::{BTreeMap, VecDeque};
+use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
+
+use context;
+use syscall::data::Event;
+
+type EventList = Weak<Mutex<VecDeque<Event>>>;
+
+type Registry = BTreeMap<(usize, usize), BTreeMap<(usize, usize), EventList>>;
+
+static REGISTRY: Once<RwLock<Registry>> = Once::new();
+
+/// Initialize registry, called if needed
+fn init_registry() -> RwLock<Registry> {
+    RwLock::new(Registry::new())
+}
+
+/// Get the global schemes list, const
+fn registry() -> RwLockReadGuard<'static, Registry> {
+    REGISTRY.call_once(init_registry).read()
+}
+
+/// Get the global schemes list, mutable
+pub fn registry_mut() -> RwLockWriteGuard<'static, Registry> {
+    REGISTRY.call_once(init_registry).write()
+}
+
+pub fn register(fd: usize, scheme_id: usize, id: usize) -> bool {
+    let (context_id, events) = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().expect("event::register: No context");
+        let context = context_lock.read();
+        (context.id, Arc::downgrade(&context.events))
+    };
+
+    let mut registry = registry_mut();
+    let entry = registry.entry((scheme_id, id)).or_insert_with(|| {
+        BTreeMap::new()
+    });
+    if entry.contains_key(&(context_id, fd)) {
+        false
+    } else {
+        entry.insert((context_id, fd), events);
+        true
+    }
+}
+
+pub fn unregister(fd: usize, scheme_id: usize, id: usize) {
+    let mut registry = registry_mut();
+
+    let mut remove = false;
+    if let Some(entry) = registry.get_mut(&(scheme_id, id)) {
+        entry.remove(&(context::context_id(), fd));
+
+        if entry.is_empty() {
+            remove = true;
+        }
+    }
+
+    if remove {
+        registry.remove(&(scheme_id, id));
+    }
+}
+
+pub fn trigger(scheme_id: usize, id: usize, flags: usize, data: usize) {
+    let registry = registry();
+    if let Some(event_lists) = registry.get(&(scheme_id, id)) {
+        for entry in event_lists.iter() {
+            if let Some(event_list_lock) = entry.1.upgrade() {
+                let mut event_list = event_list_lock.lock();
+                event_list.push_back(Event {
+                    id: (entry.0).1,
+                    flags: flags,
+                    data: data
+                });
+            }
+        }
+    }
+}
diff --git a/context/mod.rs b/context/mod.rs
index 597c301c0cca936f45457136399793218de7e81c..9da1259c1746e687a227ff72857e66dc215f3cc7 100644
--- a/context/mod.rs
+++ b/context/mod.rs
@@ -16,6 +16,9 @@ mod list;
 /// Context switch function
 mod switch;
 
+/// Event handling
+pub mod event;
+
 /// File struct - defines a scheme and a file number
 pub mod file;
 
diff --git a/scheme/debug.rs b/scheme/debug.rs
index 9b630b4bc7142da3565dab8edba0c77feb0d5f66..52b88e52c886f91e740f5ac70efd5c1944d6c187 100644
--- a/scheme/debug.rs
+++ b/scheme/debug.rs
@@ -1,11 +1,15 @@
 use collections::VecDeque;
 use core::str;
+use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
 use spin::{Mutex, Once};
 
 use context;
 use syscall::error::*;
+use syscall::flag::EVENT_READ;
 use syscall::scheme::Scheme;
 
+pub static DEBUG_SCHEME_ID: AtomicUsize = ATOMIC_USIZE_INIT;
+
 /// Input
 static INPUT: Once<Mutex<VecDeque<u8>>> = Once::new();
 
@@ -17,7 +21,13 @@ fn init_input() -> Mutex<VecDeque<u8>> {
 /// Get the global schemes list, const
 #[no_mangle]
 pub extern fn debug_input(b: u8) {
-    INPUT.call_once(init_input).lock().push_back(b)
+    let len = {
+        let mut input = INPUT.call_once(init_input).lock();
+        input.push_back(b);
+        input.len()
+    };
+
+    context::event::trigger(DEBUG_SCHEME_ID.load(Ordering::SeqCst), 0, EVENT_READ, len);
 }
 
 pub struct DebugScheme;
@@ -62,6 +72,10 @@ impl Scheme for DebugScheme {
         Ok(buffer.len())
     }
 
+    fn fevent(&self, _file: usize, flags: usize) -> Result<usize> {
+        Ok(0)
+    }
+
     fn fsync(&self, _file: usize) -> Result<usize> {
         Ok(0)
     }
diff --git a/scheme/event.rs b/scheme/event.rs
index 7801d74885ae3d0d5ffe5bd479b06c90e743b5ea..2dead8d3696e4381bae10c391eee17fb93f49ea0 100644
--- a/scheme/event.rs
+++ b/scheme/event.rs
@@ -1,33 +1,94 @@
-use core::{mem, str};
+use alloc::arc::{Arc, Weak};
+use collections::{BTreeMap, VecDeque};
+use core::mem;
+use core::sync::atomic::{AtomicUsize, Ordering};
+use spin::{Mutex, RwLock};
 
-use arch::interrupt::irq::{ACKS, COUNTS, acknowledge};
+use context;
+use syscall::data::Event;
 use syscall::error::*;
 use syscall::scheme::Scheme;
 
-pub struct EventScheme;
+pub struct EventScheme {
+    next_id: AtomicUsize,
+    handles: RwLock<BTreeMap<usize, Weak<Mutex<VecDeque<Event>>>>>
+}
+
+impl EventScheme {
+    pub fn new() -> EventScheme {
+        EventScheme {
+            next_id: AtomicUsize::new(0),
+            handles: RwLock::new(BTreeMap::new())
+        }
+    }
+}
 
 impl Scheme for EventScheme {
     fn open(&self, _path: &[u8], _flags: usize) -> Result<usize> {
-        Ok(
-    }
+        let handle = {
+            let contexts = context::contexts();
+            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+            let context = context_lock.read();
+            context.events.clone()
+        };
 
-    fn dup(&self, file: usize) -> Result<usize> {
-        Ok(file)
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(id, Arc::downgrade(&handle));
+
+        Ok(id)
     }
 
-    fn read(&self, file: usize, buffer: &mut [u8]) -> Result<usize> {
-        Ok(0)
+    fn dup(&self, id: usize) -> Result<usize> {
+        let handle = {
+            let handles = self.handles.read();
+            let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
+            handle_weak.upgrade().ok_or(Error::new(EBADF))?
+        };
+
+        let new_id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(new_id, Arc::downgrade(&handle));
+        Ok(new_id)
     }
 
-    fn write(&self, file: usize, buffer: &[u8]) -> Result<usize> {
-        Ok(0)
+    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        let handle = {
+            let handles = self.handles.read();
+            let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
+            handle_weak.upgrade().ok_or(Error::new(EBADF))?
+        };
+
+        let event_size = mem::size_of::<Event>();
+        let len = buf.len()/event_size;
+        if len > 0 {
+            loop {
+                let mut i = 0;
+                {
+                    let mut events = handle.lock();
+                    while ! events.is_empty() && i < len {
+                        let event = events.pop_front().unwrap();
+                        unsafe { *(buf.as_mut_ptr() as *mut Event).offset(i as isize) = event; }
+                        i += 1;
+                    }
+                }
+
+                if i > 0 {
+                    return Ok(i * event_size);
+                } else {
+                    unsafe { context::switch(); } //TODO: Block
+                }
+            }
+        } else {
+            Ok(0)
+        }
     }
 
-    fn fsync(&self, _file: usize) -> Result<usize> {
-        Ok(0)
+    fn fsync(&self, id: usize) -> Result<usize> {
+        let handles = self.handles.read();
+        let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
+        handle_weak.upgrade().ok_or(Error::new(EBADF)).and(Ok(0))
     }
 
-    fn close(&self, _file: usize) -> Result<usize> {
-        Ok(0)
+    fn close(&self, id: usize) -> Result<usize> {
+        self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
     }
 }
diff --git a/scheme/mod.rs b/scheme/mod.rs
index 4d0580ac07f4aae1566d7c8085d24c9c2e86a32e..7d3e25e8070619d8766f7ec6b7618c24f9ae0ba3 100644
--- a/scheme/mod.rs
+++ b/scheme/mod.rs
@@ -8,15 +8,15 @@
 
 use alloc::arc::Arc;
 use alloc::boxed::Box;
-
 use collections::BTreeMap;
-
+use core::sync::atomic::Ordering;
 use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
 
 use syscall::error::*;
 use syscall::scheme::Scheme;
 
-use self::debug::DebugScheme;
+use self::debug::{DEBUG_SCHEME_ID, DebugScheme};
+use self::event::EventScheme;
 use self::env::EnvScheme;
 use self::initfs::InitFsScheme;
 use self::irq::IrqScheme;
@@ -25,6 +25,9 @@ use self::root::RootScheme;
 /// Debug scheme
 pub mod debug;
 
+/// Kernel events
+pub mod event;
+
 /// Environmental variables
 pub mod env;
 
@@ -74,7 +77,7 @@ impl SchemeList {
     }
 
     /// Create a new scheme.
-    pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Box<Scheme + Send + Sync>>) -> Result<&Arc<Box<Scheme + Send + Sync>>> {
+    pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Box<Scheme + Send + Sync>>) -> Result<usize> {
         if self.names.contains_key(&name) {
             return Err(Error::new(EEXIST));
         }
@@ -97,7 +100,7 @@ impl SchemeList {
         assert!(self.map.insert(id, scheme).is_none());
         assert!(self.names.insert(name, id).is_none());
 
-        Ok(self.map.get(&id).expect("Failed to insert new scheme. ID is out of bounds."))
+        Ok(id)
     }
 }
 
@@ -108,7 +111,8 @@ static SCHEMES: Once<RwLock<SchemeList>> = Once::new();
 fn init_schemes() -> RwLock<SchemeList> {
     let mut list: SchemeList = SchemeList::new();
     list.insert(Box::new(*b""), Arc::new(Box::new(RootScheme::new()))).expect("failed to insert root scheme");
-    list.insert(Box::new(*b"debug"), Arc::new(Box::new(DebugScheme))).expect("failed to insert debug scheme");
+    DEBUG_SCHEME_ID.store(list.insert(Box::new(*b"debug"), Arc::new(Box::new(DebugScheme))).expect("failed to insert debug scheme"), Ordering::SeqCst);
+    list.insert(Box::new(*b"event"), Arc::new(Box::new(EventScheme::new()))).expect("failed to insert event scheme");
     list.insert(Box::new(*b"env"), Arc::new(Box::new(EnvScheme::new()))).expect("failed to insert env scheme");
     list.insert(Box::new(*b"initfs"), Arc::new(Box::new(InitFsScheme::new()))).expect("failed to insert initfs scheme");
     list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme");
diff --git a/scheme/root.rs b/scheme/root.rs
index a2e006009fd30fbd567c515b8dce406797cc43b5..e982c8f41b92c294d2c1b7827a01dc602efee7d1 100644
--- a/scheme/root.rs
+++ b/scheme/root.rs
@@ -38,7 +38,8 @@ impl Scheme for RootScheme {
                 return Err(Error::new(EEXIST));
             }
             let inner = Arc::new(UserInner::new(context));
-            schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme");
+            let id = schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme");
+            inner.scheme_id.store(id, Ordering::SeqCst);
             inner
         };
 
diff --git a/scheme/user.rs b/scheme/user.rs
index c3554562372d25feaea753f53a0dcf9c907502b6..a02b2b7bf11725a52f1b1468feb684716ad4d88e 100644
--- a/scheme/user.rs
+++ b/scheme/user.rs
@@ -15,6 +15,7 @@ use syscall::number::*;
 use syscall::scheme::Scheme;
 
 pub struct UserInner {
+    pub scheme_id: AtomicUsize,
     next_id: AtomicUsize,
     context: Weak<RwLock<Context>>,
     todo: Mutex<VecDeque<Packet>>,
@@ -24,7 +25,8 @@ pub struct UserInner {
 impl UserInner {
     pub fn new(context: Weak<RwLock<Context>>) -> UserInner {
         UserInner {
-            next_id: AtomicUsize::new(0),
+            scheme_id: AtomicUsize::new(0),
+            next_id: AtomicUsize::new(1),
             context: context,
             todo: Mutex::new(VecDeque::new()),
             done: Mutex::new(BTreeMap::new())
@@ -177,7 +179,14 @@ impl UserInner {
         let mut i = 0;
         while i < len {
             let packet = unsafe { *(buf.as_ptr() as *const Packet).offset(i as isize) };
-            self.done.lock().insert(packet.id, packet.a);
+            if packet.id == 0 {
+                match packet.a {
+                    SYS_FEVENT => context::event::trigger(self.scheme_id.load(Ordering::SeqCst), packet.b, packet.c, packet.d),
+                    _ => println!("Unknown scheme -> kernel message {}", packet.a)
+                }
+            } else {
+                self.done.lock().insert(packet.id, packet.a);
+            }
             i += 1;
         }
 
diff --git a/syscall/fs.rs b/syscall/fs.rs
index ca88b267fce6398bf95bc121fdc1612584a72912..91c36e4944ebac2f30836848d98d4c5274b9ebc8 100644
--- a/syscall/fs.rs
+++ b/syscall/fs.rs
@@ -72,6 +72,8 @@ pub fn close(fd: usize) -> Result<usize> {
         file
     };
 
+    context::event::unregister(fd, file.scheme, file.number);
+
     let scheme = {
         let schemes = scheme::schemes();
         let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
@@ -113,7 +115,9 @@ pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
         let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
         scheme.clone()
     };
-    scheme.fevent(file.number, flags)
+    scheme.fevent(file.number, flags)?;
+    context::event::register(fd, file.scheme, file.number);
+    Ok(0)
 }
 
 /// Get the canonical path of the file