From f25a16656ae9f266415abd6108b2aa048fd74f4d Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Wed, 22 Feb 2012 14:12:06 +0100
Subject: [PATCH 050/109] scsi-disk: support READ DVD STRUCTURE

RH-Author: Paolo Bonzini <pbonzini@redhat.com>
Message-id: <1329919979-20948-50-git-send-email-pbonzini@redhat.com>
Patchwork-id: 37530
O-Subject: [RHEL 6.3 qemu-kvm PATCH v2 049/102] scsi-disk: support READ DVD STRUCTURE
Bugzilla: 782029
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
RH-Acked-by: Orit Wasserman <owasserm@redhat.com>
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>

Upstream used st*_be_p functions, which are not available to
common code in RHEL6 qemu.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from ceb792ef299a1bb0144c56fab45631d17c35e7f5)
---
 hw/scsi-disk.c |  110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 109 insertions(+), 1 deletions(-)

Signed-off-by: Michal Novotny <minovotn@redhat.com>
---
 hw/scsi-disk.c |  110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 109 insertions(+), 1 deletions(-)

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 771721d..20cdb41 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -614,10 +614,118 @@ static inline bool media_is_dvd(SCSIDiskState *s)
     return nb_sectors > CD_MAX_SECTORS;
 }
 
+static inline bool media_is_cd(SCSIDiskState *s)
+{
+    uint64_t nb_sectors;
+    if (s->qdev.type != TYPE_ROM) {
+        return false;
+    }
+    if (!bdrv_is_inserted(s->bs)) {
+        return false;
+    }
+    bdrv_get_geometry(s->bs, &nb_sectors);
+    return nb_sectors <= CD_MAX_SECTORS;
+}
+
 static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
                                    uint8_t *outbuf)
 {
-    scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
+    static const int rds_caps_size[5] = {
+        [0] = 2048 + 4,
+        [1] = 4 + 4,
+        [3] = 188 + 4,
+        [4] = 2048 + 4,
+    };
+
+    uint8_t media = r->req.cmd.buf[1];
+    uint8_t layer = r->req.cmd.buf[6];
+    uint8_t format = r->req.cmd.buf[7];
+    int size = -1;
+
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+    if (media != 0) {
+        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+        return -1;
+    }
+
+    if (format != 0xff) {
+        if (s->tray_open || !bdrv_is_inserted(s->bs)) {
+            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+            return -1;
+        }
+        if (media_is_cd(s)) {
+            scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT));
+            return -1;
+        }
+        if (format >= ARRAY_SIZE(rds_caps_size)) {
+            return -1;
+        }
+        size = rds_caps_size[format];
+        memset(outbuf, 0, size);
+    }
+
+    switch (format) {
+    case 0x00: {
+        /* Physical format information */
+        uint64_t nb_sectors;
+        if (layer != 0) {
+            goto fail;
+        }
+        bdrv_get_geometry(s->bs, &nb_sectors);
+
+        outbuf[4] = 1;   /* DVD-ROM, part version 1 */
+        outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+        outbuf[6] = 1;   /* one layer, read-only (per MMC-2 spec) */
+        outbuf[7] = 0;   /* default densities */
+
+        outbuf[12] = ((nb_sectors >> 2) - 1) >> 24; /* end sector */
+        outbuf[13] = ((nb_sectors >> 2) - 1) >> 16;
+        outbuf[14] = ((nb_sectors >> 2) - 1) >> 8;
+        outbuf[15] = (nb_sectors >> 2) - 1;
+        outbuf[16] = outbuf[12]; /* l0 end sector */
+        outbuf[17] = outbuf[13];
+        outbuf[18] = outbuf[14];
+        outbuf[19] = outbuf[15];
+        break;
+    }
+
+    case 0x01: /* DVD copyright information, all zeros */
+        break;
+
+    case 0x03: /* BCA information - invalid field for no BCA info */
+        return -1;
+
+    case 0x04: /* DVD disc manufacturing information, all zeros */
+        break;
+
+    case 0xff: { /* List capabilities */
+        int i;
+        size = 4;
+        for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) {
+            if (!rds_caps_size[i]) {
+                continue;
+            }
+            outbuf[size] = i;
+            outbuf[size + 1] = 0x40; /* Not writable, readable */
+            outbuf[size + 2] = rds_caps_size[i] >> 8;
+            outbuf[size + 3] = rds_caps_size[i];
+            size += 4;
+        }
+        break;
+     }
+
+    default:
+        return -1;
+    }
+
+    /* Size of buffer, not including 2 byte size field */
+    outbuf[0] = (size - 2) >> 8;
+    outbuf[1] = size - 2;
+    return size;
+
+fail:
     return -1;
 }
 
-- 
1.7.7.6

