The current raw block drvice code fakes a TOC with one track covering
the entire disc.  With this patch it attempts to read the TOC from a
cdrom device and make that information available to the ide driver
when the guest issues an ATAPI READ TOC/PMA/ATIP type 0 (read TOC)
command.

There is only support for reading the TOC on the BSDs, MacOS X, and
Linux, Win32 and other OSes will still fake one large data track.  All
tracks are reported as data, no attempt was made to support audio
tracks.  The ATAPI READ TOC/PMA/ATIP type 2 (read full TOC) command is
unchanged, I did not have a way of testing it.

The purpose of this patch, in case anyone was wondering, was to get
the BeOS R5 CD booting in qemu.

 -jre
Index: block-raw.c
===================================================================
RCS file: /sources/qemu/qemu/block-raw.c,v
retrieving revision 1.10
diff -u -r1.10 block-raw.c
--- block-raw.c 3 Sep 2006 12:08:37 -0000       1.10
+++ block-raw.c 18 Oct 2006 23:17:33 -0000
@@ -39,6 +39,7 @@
 #include <IOKit/storage/IOMediaBSDClient.h>
 #include <IOKit/storage/IOMedia.h>
 #include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/storage/IOCDMediaBSDClient.h>
 //#include <IOKit/storage/IOCDTypes.h>
 #include <CoreFoundation/CoreFoundation.h>
 #endif
@@ -51,6 +52,10 @@
 #include <linux/cdrom.h>
 #include <linux/fd.h>
 #endif
+#if defined(_BSD) && !defined(CONFIG_COCOA)
+#include <sys/cdio.h>
+#include <arpa/inet.h>
+#endif
 
 //#define DEBUG_FLOPPY
 
@@ -675,6 +680,114 @@
 }
 #endif
 
+static void raw_gettracks(BlockDriverState *bs, int *start, int *end)
+{
+    BDRVRawState *s = bs->opaque;
+    int fd = s->fd;
+#ifdef CDROMREADTOCHDR
+    struct cdrom_tochdr hdr;
+#endif
+#ifdef CDIOREADTOCHEADER
+    struct ioc_toc_header hdr;
+#endif
+#ifdef DKIOCCDREADTOC
+    dk_cd_read_toc_t hdr;
+    uint8_t buf[4];
+#endif
+    int ret;
+
+    *start = -1;
+    *end = -1;
+    ret = fd_open(bs);
+    if (ret < 0)
+        return;
+
+#if defined(CDROMREADTOCHDR)
+    if (!ioctl(fd, CDROMREADTOCHDR, &hdr)) {
+        *start = hdr.cdth_trk0;
+        *end = hdr.cdth_trk1;
+    }
+#endif
+#ifdef CDIOREADTOCHEADER
+    if (!ioctl(fd, CDIOREADTOCHEADER, &hdr)) {
+        *start = hdr.starting_track;
+        *end = hdr.ending_track;
+    }
+#endif
+#if defined(DKIOCCDREADTOC)
+    memset(&hdr, 0, sizeof(hdr));
+    hdr.bufferLength = sizeof(buf);
+    hdr.buffer = &buf;
+    if (!ioctl(fd, DKIOCCDREADTOC, &hdr) && sizeof(buf) == hdr.bufferLength) {
+        /* buf is the same format as an ATAPI READ TOC/PMA/ATIP response */
+        *start = buf[2];
+        *end = buf[3];
+    }
+#endif
+
+    return;
+}
+
+static int64_t raw_gettrackoff(BlockDriverState *bs, int track)
+{
+    BDRVRawState *s = bs->opaque;
+    int fd = s->fd;
+    int64_t off;
+#if defined(CDIOREADTOCENTRY)
+    struct ioc_read_toc_single_entry entry;
+#elif defined(CDIOREADTOCENTRIES)
+    struct ioc_read_toc_entry entry;
+    struct cd_toc_entry buf;
+#elif defined(CDROMREADTOCENTRY)
+    struct cdrom_tocentry entry;
+#elif defined(DKIOCCDREADTOC)
+    dk_cd_read_toc_t hdr;
+    uint8_t buf[804];
+    int skip, max;
+#endif
+    int ret;
+
+    off = -1;
+    ret = fd_open(bs);
+    if (ret < 0)
+        return off;
+
+#if defined(CDIOREADTOCENTRY)
+    entry.address_format = CD_LBA_FORMAT;
+    entry.track = track;
+    if (!ioctl(fd, CDIOREADTOCENTRY, &entry))
+        off = ntohl(entry.entry.addr.lba);
+#elif defined(CDIOREADTOCENTRIES)
+    entry.address_format = CD_LBA_FORMAT;
+    entry.starting_track = track;
+    entry.data_len = sizeof(buf);
+    entry.data = &buf;
+    if (!ioctl(fd, CDIOREADTOCENTRIES, &entry))
+        off = buf.addr.lba;
+#elif defined(CDROMREADTOCENTRY)
+    entry.cdte_track = track;
+    entry.cdte_format = CDROM_LBA;
+    if (!ioctl(fd, CDROMREADTOCENTRY, &entry))
+        off = entry.cdte_addr.lba;
+#elif defined(DKIOCCDREADTOC)
+    memset(&hdr, 0, sizeof(hdr));
+    hdr.address.track = track;
+    hdr.bufferLength = sizeof(buf);
+    hdr.buffer = &buf;
+    if (!ioctl(fd, DKIOCCDREADTOC, &hdr) && 4 <= hdr.bufferLength) {
+        /* buf is the same format as an ATAPI READ TOC/PMA/ATIP response */
+        max = OSReadBigInt16(buf, 0) + 2;
+        if (sizeof(buf) < max)
+            max = sizeof(buf);
+        for (skip = 4; 0 > off && skip + 8 <= max; skip += 8)
+            if(track == buf[skip + 2])
+                off = OSReadBigInt32(buf, skip + 4);
+    }
+#endif
+
+    return off;
+}
+
 #if defined(__linux__)
 
 static int raw_is_inserted(BlockDriverState *bs)
@@ -818,6 +931,8 @@
     .bdrv_pread = raw_pread,
     .bdrv_pwrite = raw_pwrite,
     .bdrv_getlength = raw_getlength,
+    .bdrv_gettracks = raw_gettracks,
+    .bdrv_gettrackoff = raw_gettrackoff,
 
     /* removable device support */
     .bdrv_is_inserted = raw_is_inserted,
Index: block.c
===================================================================
RCS file: /sources/qemu/qemu/block.c,v
retrieving revision 1.37
diff -u -r1.37 block.c
--- block.c     24 Aug 2006 19:53:37 -0000      1.37
+++ block.c     18 Oct 2006 23:17:34 -0000
@@ -705,6 +705,38 @@
     *nb_sectors_ptr = length;
 }
 
+void bdrv_get_track_count(BlockDriverState *bs, int *start, int *end)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        *start = *end = 0;
+    else if (!drv->bdrv_gettracks)
+        *start = *end = 1;
+    else
+        drv->bdrv_gettracks(bs, start, end);
+}
+
+int64_t bdrv_get_track_offset(BlockDriverState *bs, int track)
+{
+    BlockDriver *drv = bs->drv;
+    int64_t ret;
+
+    if (!drv)
+        ret = -1;
+    else if (!drv->bdrv_gettrackoff) {
+        if (1 == track)
+            ret = 0;
+        else if(0xaa == track)
+            bdrv_get_geometry(bs, &ret);
+        else
+            ret = -1;
+    }
+    else
+        ret = drv->bdrv_gettrackoff(bs, track);
+
+    return ret;
+}
+
 /* force a given boot sector. */
 void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size)
 {
Index: block_int.h
===================================================================
RCS file: /sources/qemu/qemu/block_int.h,v
retrieving revision 1.10
diff -u -r1.10 block_int.h
--- block_int.h 19 Aug 2006 11:45:59 -0000      1.10
+++ block_int.h 18 Oct 2006 23:17:34 -0000
@@ -58,6 +58,8 @@
                        const uint8_t *buf, int count);
     int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
     int64_t (*bdrv_getlength)(BlockDriverState *bs);
+    void (*bdrv_gettracks)(BlockDriverState *bs, int *start, int *end);
+    int64_t (*bdrv_gettrackoff)(BlockDriverState *bs, int track);
     int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, 
                                  const uint8_t *buf, int nb_sectors);
 
Index: vl.h
===================================================================
RCS file: /sources/qemu/qemu/vl.h,v
retrieving revision 1.154
diff -u -r1.154 vl.h
--- vl.h        24 Sep 2006 18:49:43 -0000      1.154
+++ vl.h        18 Oct 2006 23:17:35 -0000
@@ -564,6 +564,8 @@
 int bdrv_truncate(BlockDriverState *bs, int64_t offset);
 int64_t bdrv_getlength(BlockDriverState *bs);
 void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr);
+void bdrv_get_track_count(BlockDriverState *bs, int *start, int *end);
+int64_t bdrv_get_track_offset(BlockDriverState *bs, int track);
 int bdrv_commit(BlockDriverState *bs);
 void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size);
 /* async block I/O */
@@ -882,7 +884,7 @@
                    SetIRQFunc *set_irq, void *irq_opaque, int irq);
 
 /* cdrom.c */
-int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc(BlockDriverState *bs, uint8_t *buf, int msf, int 
start_track);
 int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
 
 /* es1370.c */
Index: hw/cdrom.c
===================================================================
RCS file: /sources/qemu/qemu/hw/cdrom.c,v
retrieving revision 1.1
diff -u -r1.1 cdrom.c
--- hw/cdrom.c  25 May 2006 23:58:51 -0000      1.1
+++ hw/cdrom.c  18 Oct 2006 23:17:35 -0000
@@ -37,42 +37,55 @@
 
 /* same toc as bochs. Return -1 if error or the toc length */
 /* XXX: check this */
-int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
+int cdrom_read_toc(BlockDriverState *bs, uint8_t *buf, int msf, int 
start_track)
 {
     uint8_t *q;
-    int len;
-    
-    if (start_track > 1 && start_track != 0xaa)
+    int start, end, len;
+    int64_t off;
+
+    bdrv_get_track_count(bs, &start, &end);
+    if (0 > start || 0 > end)
+        return -1;
+    if (0 == start_track)
+        start_track = start;
+    if (start_track < start || (start_track > end && start_track != 0xaa))
         return -1;
     q = buf + 2;
-    *q++ = 1; /* first session */
-    *q++ = 1; /* last session */
-    if (start_track <= 1) {
+    *q++ = start; /* first session */
+    *q++ = end; /* last session */
+    while (start_track <= end) {
+        off = bdrv_get_track_offset(bs, start_track);
+        if (0 > off)
+            return -1;
         *q++ = 0; /* reserved */
-        *q++ = 0x14; /* ADR, control */
-        *q++ = 1;    /* track number */
+        *q++ = 0x14; /* ADR, control XXX audio tracks are not supported */
+        *q++ = start_track; /* track number */
         *q++ = 0; /* reserved */
         if (msf) {
             *q++ = 0; /* reserved */
-            lba_to_msf(q, 0);
+            lba_to_msf(q, off);
             q += 3;
         } else {
-            /* sector 0 */
-            cpu_to_be32wu((uint32_t *)q, 0);
+            /* start sector */
+            cpu_to_be32wu((uint32_t *)q, off);
             q += 4;
         }
+        start_track++;
     }
     /* lead out track */
+    off = bdrv_get_track_offset(bs, 0xaa);
+    if (0 > off)
+        return -1;
     *q++ = 0; /* reserved */
     *q++ = 0x16; /* ADR, control */
     *q++ = 0xaa; /* track number */
     *q++ = 0; /* reserved */
     if (msf) {
         *q++ = 0; /* reserved */
-        lba_to_msf(q, nb_sectors);
+        lba_to_msf(q, off);
         q += 3;
     } else {
-        cpu_to_be32wu((uint32_t *)q, nb_sectors);
+        cpu_to_be32wu((uint32_t *)q, off);
         q += 4;
     }
     len = q - buf;
Index: hw/ide.c
===================================================================
RCS file: /sources/qemu/qemu/hw/ide.c,v
retrieving revision 1.48
diff -u -r1.48 ide.c
--- hw/ide.c    19 Aug 2006 11:44:21 -0000      1.48
+++ hw/ide.c    18 Oct 2006 23:17:37 -0000
@@ -1415,7 +1415,7 @@
             start_track = packet[6];
             switch(format) {
             case 0:
-                len = cdrom_read_toc(total_sectors, buf, msf, start_track);
+                len = cdrom_read_toc(s->bs, buf, msf, start_track);
                 if (len < 0)
                     goto error_cmd;
                 ide_atapi_cmd_reply(s, len, max_len);

Attachment: pgpbKue5k0Q1W.pgp
Description: PGP signature

_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to