Index: qemu/block-raw-posix.c
@@ -55,6 +55,7 @@
 #ifdef __FreeBSD__
 #include <signal.h>
 #include <sys/disk.h>
+#include <sys/cdio.h>
 #endif
 
 #ifdef __OpenBSD__
@@ -105,6 +106,9 @@
     int fd_got_error;
     int fd_media_changed;
 #endif
+#if defined(__FreeBSD__)
+    int cd_open_flags;
+#endif
     uint8_t* aligned_buf;
 } BDRVRawState;
 
@@ -112,6 +116,12 @@
 
 static int fd_open(BlockDriverState *bs);
 
+#if defined(__FreeBSD__)
+static int cd_open(BlockDriverState *bs);
+#endif
+
+static int raw_is_inserted(BlockDriverState *bs);
+
 static int raw_open(BlockDriverState *bs, const char *filename, int flags)
 {
     BDRVRawState *s = bs->opaque;
@@ -747,6 +757,9 @@
     int64_t size;
 #ifdef _BSD
     struct stat sb;
+#ifdef __FreeBSD__
+    int reopened = 0;
+#endif
 #endif
 #ifdef __sun__
     struct dk_minfo minfo;
@@ -759,6 +772,9 @@
         return ret;
 
 #ifdef _BSD
+#ifdef __FreeBSD__
+again:
+#endif
     if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
 #ifdef DIOCGMEDIASIZE
 	if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
@@ -768,6 +784,19 @@
 #else
         size = lseek(fd, 0LL, SEEK_END);
 #endif
+#ifdef __FreeBSD__
+        switch(s->type) {
+        case FTYPE_CD:
+            /* XXX FreeBSD acd returns UINT_MAX sectors for an empty drive */
+            if (size == 2048LL * (unsigned)-1)
+                size = 0;
+            /* XXX no disc?  maybe we need to reopen... */
+            if (size <= 0 && !reopened && cd_open(bs) >= 0) {
+                reopened = 1;
+                goto again;
+            }
+        }
+#endif
     } else
 #endif
 #ifdef __sun__
@@ -958,6 +987,14 @@
         bs->sg = 1;
     }
 #endif
+#if defined(__FreeBSD__)
+    if (strstart(filename, "/dev/cd", NULL) ||
+        strstart(filename, "/dev/acd", NULL)) {
+        s->type = FTYPE_CD;
+        s->cd_open_flags = open_flags;
+    }
+#endif
+    s->fd = -1;
     fd = open(filename, open_flags, 0644);
     if (fd < 0) {
         ret = -errno;
@@ -966,6 +1003,11 @@
         return ret;
     }
     s->fd = fd;
+#if defined(__FreeBSD__)
+    /* make sure the door isnt locked at this time */
+    if (s->type == FTYPE_CD)
+        ioctl (s->fd, CDIOCALLOW);
+#endif
 #if defined(__linux__)
     /* close fd so that we can reopen it as needed */
     if (s->type == FTYPE_FD) {
@@ -1132,7 +1174,116 @@
 
     return ioctl(s->fd, req, buf);
 }
-#else
+#elif defined(__FreeBSD__)
+
+static int fd_open(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+
+    /* this is just to ensure s->fd is sane (its called by io ops) */
+    if (s->fd >= 0)
+        return 0;
+    return -EIO;
+}
+
+static int cd_open(BlockDriverState *bs)
+{
+#if defined(__FreeBSD__)
+    BDRVRawState *s = bs->opaque;
+    int fd;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        /* XXX force reread of possibly changed/newly loaded disc,
+         * FreeBSD seems to not notice sometimes... */
+        if (s->fd >= 0)
+            close (s->fd);
+        fd = open(bs->filename, s->cd_open_flags, 0644);
+        if (fd < 0) {
+            s->fd = -1;
+            return -EIO;
+        }
+        s->fd = fd;
+        /* make sure the door isnt locked at this time */
+        ioctl (s->fd, CDIOCALLOW);
+    }
+#endif
+    return 0;
+}
+
+static int raw_is_inserted(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        return (raw_getlength(bs) > 0);
+    case FTYPE_FD:
+        /* XXX handle this */
+        /* FALLTHRU */
+    default:
+        return 1;
+    }
+}
+
+static int raw_media_changed(BlockDriverState *bs)
+{
+    return -ENOTSUP;
+}
+
+static int raw_eject(BlockDriverState *bs, int eject_flag)
+{
+    BDRVRawState *s = bs->opaque;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        if (s->fd < 0)
+            return -ENOTSUP;
+        (void) ioctl (s->fd, CDIOCALLOW);
+        if (eject_flag) {
+            if (ioctl (s->fd, CDIOCEJECT) < 0)
+                perror("CDIOCEJECT");
+        } else {
+            if (ioctl (s->fd, CDIOCCLOSE) < 0)
+                perror("CDIOCCLOSE");
+        }
+        if (cd_open(bs) < 0)
+            return -ENOTSUP;
+        break;
+    case FTYPE_FD:
+        /* XXX handle this */
+        /* FALLTHRU */
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static int raw_set_locked(BlockDriverState *bs, int locked)
+{
+    BDRVRawState *s = bs->opaque;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        if (s->fd < 0)
+            return -ENOTSUP;
+        if (ioctl (s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) {
+            /* Note: an error can happen if the distribution automatically
+               mounts the CD-ROM */
+            //        perror("CDROM_LOCKDOOR");
+        }
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+    return -ENOTSUP;
+}
+#else /* !linux && !FreeBSD */
 
 static int fd_open(BlockDriverState *bs)
 {
@@ -1163,7 +1314,7 @@
 {
     return -ENOTSUP;
 }
-#endif /* !linux */
+#endif /* !linux && !FreeBSD */
 
 BlockDriver bdrv_host_device = {
     "host_device",
Index: qemu/hw/scsi-disk.c
@@ -417,16 +417,26 @@
     switch (command) {
     case 0x0:
 	DPRINTF("Test Unit Ready\n");
+        if (!bdrv_is_inserted(s->bdrv))
+            goto notready;
 	break;
     case 0x03:
         DPRINTF("Request Sense (len %d)\n", len);
         if (len < 4)
             goto fail;
         memset(outbuf, 0, 4);
+        r->buf_len = 4;
+        if (s->sense == SENSE_NOT_READY && len >= 18) {
+            memset(outbuf, 0, 18);
+            r->buf_len = 18;
+            outbuf[7] = 10;
+            /* asc 0x3a, ascq 0: Medium not present */
+            outbuf[12] = 0x3a;
+            outbuf[13] = 0;
+        }
         outbuf[0] = 0xf0;
         outbuf[1] = 0;
         outbuf[2] = s->sense;
-        r->buf_len = 4;
         break;
     case 0x12:
         DPRINTF("Inquiry (len %d)\n", len);
@@ -725,6 +735,10 @@
         break;
     case 0x1b:
         DPRINTF("Start Stop Unit\n");
+        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM &&
+            (buf[4] & 2))
+            /* load/eject medium */
+            bdrv_eject(s->bdrv, !(buf[4] & 1));
 	break;
     case 0x1e:
         DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
@@ -754,6 +768,7 @@
             outbuf[7] = 0;
             r->buf_len = 8;
         } else {
+        notready:
             scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
             return 0;
         }
@@ -790,6 +805,7 @@
             start_track = buf[6];
             bdrv_get_geometry(s->bdrv, &nb_sectors);
             DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+            nb_sectors /= s->cluster_size;
             switch(format) {
             case 0:
                 toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
