diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
index af586ec..e414128 100644
--- a/QMP/qmp-events.txt
+++ b/QMP/qmp-events.txt
@@ -26,6 +26,24 @@ Example:
 Note: If action is "stop", a STOP event will eventually follow the
 BLOCK_IO_ERROR event.
 
+BLOCK_MEDIUM_EJECT
+------------------
+
+Emitted when the guest succeeds ejecting a medium. If the device has a tray,
+then this event is emitted whenever the guest opens or closes the tray.
+
+Data:
+
+- "device": device name (json-string)
+- "ejected": true if the tray has been opened or false if it has been closed
+
+Example:
+
+{ "event": "BLOCK_MEDIUM_EJECT",
+    "data": { "device": "ide1-cd1",
+              "ejected": true },
+    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
 RESET
 -----
 
diff --git a/block.c b/block.c
index 3f072f6..b25932b 100644
--- a/block.c
+++ b/block.c
@@ -1998,6 +1998,18 @@ BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read)
     return is_read ? bs->on_read_error : bs->on_write_error;
 }
 
+void bdrv_dev_set_medium_eject_notify(BlockDriverState *bs,
+                                      bdrv_dev_medium_eject_notify_cb cb)
+{
+    bs->dev_medium_eject_notify_cb = cb;
+}
+
+void bdrv_dev_medium_eject_notify(BlockDriverState *bs, int is_ejected)
+{
+    assert(bs->dev_medium_eject_notify_cb);
+    bs->dev_medium_eject_notify_cb(bs, is_ejected);
+}
+
 int bdrv_is_read_only(BlockDriverState *bs)
 {
     return bs->read_only;
diff --git a/block.h b/block.h
index 3bd4398..344ca0d 100644
--- a/block.h
+++ b/block.h
@@ -247,6 +247,11 @@ int bdrv_get_translation_hint(BlockDriverState *bs);
 void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
                        BlockErrorAction on_write_error);
 BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read);
+typedef void (*bdrv_dev_medium_eject_notify_cb)(BlockDriverState *bs,
+                                                int is_ejected);
+void bdrv_dev_set_medium_eject_notify(BlockDriverState *bs,
+                                      bdrv_dev_medium_eject_notify_cb cb);
+void bdrv_dev_medium_eject_notify(BlockDriverState *bs, int is_ejected);
 int bdrv_is_read_only(BlockDriverState *bs);
 int bdrv_is_sg(BlockDriverState *bs);
 int bdrv_enable_write_cache(BlockDriverState *bs);
diff --git a/block_int.h b/block_int.h
index 311bd2a..50c34f6 100644
--- a/block_int.h
+++ b/block_int.h
@@ -260,6 +260,9 @@ struct BlockDriverState {
     QTAILQ_ENTRY(BlockDriverState) list;
     void *private;
 
+    /* Callback for medium eject */
+    bdrv_dev_medium_eject_notify_cb dev_medium_eject_notify_cb;
+
     QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
 };
 
diff --git a/blockdev.c b/blockdev.c
index 1f83c88..71b50fa 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -11,6 +11,7 @@
 #include "blockdev.h"
 #include "monitor.h"
 #include "qerror.h"
+#include "qjson.h"
 #include "qemu-option.h"
 #include "qemu-config.h"
 #include "sysemu.h"
@@ -237,6 +238,17 @@ static bool do_check_io_limits(BlockIOLimit *io_limits)
     return true;
 }
 
+static void on_medium_eject(BlockDriverState *bs, int is_ejected)
+{
+    QObject *data;
+
+    data = qobject_from_jsonf("{ 'device': %s, 'ejected': %i }",
+                              bdrv_get_device_name(bs), is_ejected);
+    monitor_protocol_event(QEVENT_BLOCK_MEDIUM_EJECT, data);
+
+    qobject_decref(data);
+}
+
 DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
 {
     const char *buf;
@@ -503,6 +515,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
     QTAILQ_INSERT_TAIL(&drives, dinfo, next);
 
     bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
+    bdrv_dev_set_medium_eject_notify(dinfo->bdrv, on_medium_eject);
 
     /* disk I/O throttling */
     bdrv_set_io_limits(dinfo->bdrv, &io_limits);
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 0adb27b..4b4bc61 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -885,6 +885,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
         }
         bdrv_eject(s->bs, !start);
         s->tray_open = !start;
+        bdrv_dev_medium_eject_notify(s->bs, !start);
     }
 
     ide_atapi_cmd_ok(s);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 5d8bf53..35e55f4 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1061,6 +1061,7 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
         }
         bdrv_eject(s->qdev.conf.bs, !start);
         s->tray_open = !start;
+        bdrv_dev_medium_eject_notify(s->qdev.conf.bs, !start);
     }
     return 0;
 }
diff --git a/monitor.c b/monitor.c
index 187083c..df6b475 100644
--- a/monitor.c
+++ b/monitor.c
@@ -479,6 +479,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
         case QEVENT_SPICE_DISCONNECTED:
             event_name = "SPICE_DISCONNECTED";
             break;
+        case QEVENT_BLOCK_MEDIUM_EJECT:
+            event_name = "BLOCK_MEDIUM_EJECT";
+            break;
         default:
             abort();
             break;
diff --git a/monitor.h b/monitor.h
index 887c472..16fab50 100644
--- a/monitor.h
+++ b/monitor.h
@@ -36,6 +36,7 @@ typedef enum MonitorEvent {
     QEVENT_SPICE_CONNECTED,
     QEVENT_SPICE_INITIALIZED,
     QEVENT_SPICE_DISCONNECTED,
+    QEVENT_BLOCK_MEDIUM_EJECT,
     QEVENT_MAX,
 } MonitorEvent;
 
