Index: scsi/cd.c
===================================================================
RCS file: /cvs/src/sys/scsi/cd.c,v
retrieving revision 1.157
diff -u scsi/cd.c
--- scsi/cd.c	16 Dec 2009 10:51:28 -0000	1.157
+++ scsi/cd.c	18 Mar 2010 20:06:07 -0000
@@ -67,6 +67,9 @@
 #include <sys/conf.h>
 #include <sys/scsiio.h>
 #include <sys/vnode.h>
+#include <sys/hotplug.h>
+#include <sys/kernel.h>
+#include <sys/workq.h>
 
 #include <scsi/scsi_all.h>
 #include <scsi/cd.h>
@@ -76,6 +79,8 @@
 
 #include <ufs/ffs/fs.h>		/* for BBSIZE and SBSIZE */
 
+#include "hotplug.h"
+
 #define	CDOUTSTANDING	4
 
 #define MAXTRACK	99
@@ -114,6 +119,7 @@
 	struct mutex sc_queue_mtx;
 	struct mutex sc_start_mtx;
 	struct timeout sc_timeout;
+	struct timeout sc_mediatick;	/* media check timer */
 	void *sc_cdpwrhook;		/* our power hook */
 };
 
@@ -150,6 +156,10 @@
 int	dvd_read_manufact(struct cd_softc *, union dvd_struct *);
 int	dvd_read_struct(struct cd_softc *, union dvd_struct *);
 
+void	cd_mediatick(void *);
+void	cd_mediacheck(void *, void *);
+int	cd_get_media_event(struct cd_softc *);
+
 void	cd_powerhook(int why, void *arg);
 
 #if defined(__macppc__)
@@ -246,6 +256,11 @@
 
 	timeout_set(&sc->sc_timeout, cdstart, sc);
 
+#if NHOTPLUG > 0
+	timeout_set(&sc->sc_mediatick, cd_mediatick, sc);
+	timeout_add(&sc->sc_mediatick, hz);
+#endif
+
 	if ((sc->sc_cdpwrhook = powerhook_establish(cd_powerhook, sc)) == NULL)
 		printf("%s: WARNING: unable to establish power hook\n",
 		    sc->sc_dev.dv_xname);
@@ -293,6 +308,10 @@
 	if (sc->sc_cdpwrhook != NULL)
 		powerhook_disestablish(sc->sc_cdpwrhook);
 
+#if NHOTPLUG > 0
+	timeout_del(&sc->sc_mediatick);
+#endif
+
 	/* Detach disk. */
 	disk_detach(&sc->sc_dk);
 
@@ -1947,6 +1966,75 @@
 		return (EINVAL);
 	}
 }
+
+#if NHOTPLUG > 0
+void
+cd_mediatick(void *arg)
+{
+	workq_add_task(NULL, 0, cd_mediacheck, arg, NULL);
+}
+
+void
+cd_mediacheck(void *arg1, void *arg2)
+{
+	struct cd_softc *sc = arg1;
+	int event;
+
+	event = cd_get_media_event(sc);
+
+	switch (event) {
+	case -1:
+		/* scsi error, not fatal */
+		break;
+	case -2:
+		/* media events not supported, fatal */
+		return;
+	case MEDIA_EJECT:
+		hotplug_device_detach(DV_DISK, sc->sc_dev.dv_xname);
+		break;
+	case MEDIA_NEW:
+		hotplug_device_attach(DV_DISK, sc->sc_dev.dv_xname);
+		break;
+	}
+
+	timeout_add(&sc->sc_mediatick, hz);
+}
+
+int
+cd_get_media_event(struct cd_softc *sc)
+{
+	struct scsi_get_event_status cmd;
+	struct scsi_event_hdr *hdr;
+	struct scsi_media_event_data *me;
+	void *buf;
+	int data_len = sizeof(*hdr) + sizeof(*me);
+	int error;
+
+	buf = malloc(data_len, M_TEMP, M_WAITOK);
+	if (buf == NULL)
+		return (-1);
+	hdr = buf;
+	me = (struct scsi_media_event_data *)(hdr + 1);
+
+	bzero(&cmd, sizeof(cmd));
+	cmd.opcode = GET_EVENT_STATUS;
+	cmd.immed = 1;
+	cmd.evclass = EVENT_MEDIAST;
+	_lto2b(data_len, cmd.data_len);
+
+	error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
+	    sizeof(cmd), buf, data_len, 1, 1000, NULL, SCSI_DATA_IN);
+	if (error)
+		error = -1;
+	else if (hdr->nea)
+		error = -2;
+	else
+		error = me->event;
+
+	free(buf, M_TEMP);
+	return (error);
+}
+#endif	/* NHOTPLUG > 0 */
 
 void
 cd_powerhook(int why, void *arg)
Index: scsi/cd.h
===================================================================
RCS file: /cvs/src/sys/scsi/cd.h,v
retrieving revision 1.25
diff -u scsi/cd.h
--- scsi/cd.h	16 Dec 2009 10:51:28 -0000	1.25
+++ scsi/cd.h	18 Mar 2010 20:06:07 -0000
@@ -183,6 +183,23 @@
 	u_int8_t control;
 };
 
+struct scsi_get_event_status {
+	u_int8_t opcode;
+	u_int    immed:1,
+	         reserved:7;
+	u_int8_t reserved2[2];
+	u_int8_t evclass;
+#define EVENT_OPCHANGE	0x02
+#define EVENT_PWRMGMT	0x04
+#define EVENT_EXTREQ	0x08
+#define EVENT_MEDIAST	0x10
+#define EVENT_MULTINIT	0x20
+#define EVENT_DEVBUSY	0x40
+	u_int8_t reserved3[2];
+	u_int8_t data_len[2];
+	u_int8_t control;
+};
+
 /*
  * Opcodes
  */
@@ -194,6 +211,7 @@
 #define PLAY_MSF		0x47	/* cdrom play Min,Sec,Frames mode */
 #define PLAY_TRACK		0x48	/* cdrom play track/index mode */
 #define PLAY_TRACK_REL		0x49	/* cdrom play track/index mode */
+#define GET_EVENT_STATUS	0x4a	/* get event/status notification */
 #define PAUSE			0x4b	/* cdrom pause in 'play audio' mode */
 #define READ_TRACK_INFO		0x52	/* read track/rzone info */
 #define CLOSE_TRACK		0x5b	/* close track/rzone/session/border */
@@ -268,6 +286,28 @@
 	u_int8_t	len[2];		/* Big-endian length of valid data. */
 	u_int8_t	reserved[2];
 	u_int8_t	data[2048];
+};
+
+struct scsi_event_hdr {
+	u_int8_t data_len[2];
+	u_int    evclass:3,
+	         reserved:4,
+	         nea:1;
+	u_int8_t supclass;
+};
+
+struct scsi_media_event_data {
+	u_int    event:4,
+#define MEDIA_EJECT	0x01
+#define MEDIA_NEW	0x02
+#define MEDIA_REMOVE	0x03
+#define MEDIA_CHANGE	0x04
+	         reserved:4;
+	u_int    dooropen:1,
+	         mediapresent:1,
+	         reserved2:6;
+	u_int8_t startslot;
+	u_int8_t endslot;
 };
 
 #endif
