want to test a patch? (was: Re: qemu 0.10.0 - cd/dvd drive access problems)

Juergen Lock nox at jelal.kn-bremen.de
Sat Mar 21 13:20:38 PDT 2009


In article <200903092235.n29MZCFF014159 at saturn.kn-bremen.de> I write:
>In article <alpine.BSF.2.00.0903091404100.40723 at ync.qbhto.arg> you write:
>>On Mon, 9 Mar 2009, Gary Jennejohn wrote:
>>
>>> On Sun, 08 Mar 2009 18:00:25 -0700
>>> Doug Barton <dougb at FreeBSD.org> wrote:
>[...]

>>> One thing I've noticed is that the DVD drive is locked and I can't open
>>> the drawer once qemu starts, even if there is no disc in the drive.
>>> Apparently openSUSE acceses the drive (i.e. locks it) even when it's
>>> empty.
>>
I'm not sure but this seems to be a bug in our acd driver, it seems to
lock the door when you open the device node regardless if there is a
disc in the drive or not...

>>I forgot to mention this bit. If I click the eject menu option on the 
>>CD/DVD in windows it doesn't actually eject it like native windows does, 
>>but it does allow me to remove the disc manually. After that even if I do 
>>reinsert a disc the windows guest doesn't "see" it.
>>
> Have you tried the mentioned `change ide1-cd0' in qemu's monitor?
>(This probably would benefit from scsi passthru as well...)

 After finding out that there actually is code in qemu now that attempts
to support these things on Linux I played with that a bit and came up with
the patch below that adds some FreeBSD support.  You will still have to
tell the guest to eject the disc when you want to take it out/change it
(or do the mentioned change command in the monitor if the guest doesnt
have an eject command), but other than that a passed cd drive should
now work like it now does on Linux hosts.  I also did some fixes
to the scsi cdrom emulation while I was at it (eject/load command
support was missing too), so passing a cd drive as
-drive if=scsi,media=cdrom,file=/dev/acd0 (or also /dev/cd0) should now
work as well.  (It is still readonly tho, scsi passthru support would be
the next thing to be ported i.e. so you could do file=/dev/pass0...)

 Enjoy,
	Juergen

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);


More information about the freebsd-emulation mailing list