diff options
author | ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-07-03 03:57:14 +0000 |
---|---|---|
committer | ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-07-03 03:57:14 +0000 |
commit | 8114e9e8fd2610b96a808cdf703127eb26eb5278 (patch) | |
tree | fe494e7d59c0c2291094a7fb6c992f66375d60fc | |
parent | 69dd867364a5554a6f2ae6d0e2dc7c67690c067f (diff) |
Fix ATAPI read drive structure command, by Alex Williamson.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4831 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r-- | hw/ide.c | 183 |
1 files changed, 144 insertions, 39 deletions
@@ -351,6 +351,7 @@ #define ASC_ILLEGAL_OPCODE 0x20 #define ASC_LOGICAL_BLOCK_OOR 0x21 #define ASC_INV_FIELD_IN_CMD_PACKET 0x24 +#define ASC_INCOMPATIBLE_FORMAT 0x30 #define ASC_MEDIUM_NOT_PRESENT 0x3a #define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 @@ -434,6 +435,22 @@ typedef struct IDEState { int media_changed; } IDEState; +/* XXX: DVDs that could fit on a CD will be reported as a CD */ +static inline int media_present(IDEState *s) +{ + return (s->nb_sectors > 0); +} + +static inline int media_is_dvd(IDEState *s) +{ + return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS); +} + +static inline int media_is_cd(IDEState *s) +{ + return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); +} + #define BM_STATUS_DMAING 0x01 #define BM_STATUS_ERROR 0x02 #define BM_STATUS_INT 0x04 @@ -1364,6 +1381,93 @@ static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index, return 4; } +static int ide_dvd_read_structure(IDEState *s, int format, + const uint8_t *packet, uint8_t *buf) +{ + switch (format) { + case 0x0: /* Physical format information */ + { + int layer = packet[6]; + uint64_t total_sectors; + + if (layer != 0) + return -ASC_INV_FIELD_IN_CMD_PACKET; + + bdrv_get_geometry(s->bs, &total_sectors); + total_sectors >>= 2; + if (total_sectors == 0) + return -ASC_MEDIUM_NOT_PRESENT; + + buf[4] = 1; /* DVD-ROM, part version 1 */ + buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ + buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ + buf[7] = 0; /* default densities */ + + /* FIXME: 0x30000 per spec? */ + cpu_to_ube32(buf + 8, 0); /* start sector */ + cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */ + cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */ + + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 2048 + 2); + + /* 2k data + 4 byte header */ + return (2048 + 4); + } + + case 0x01: /* DVD copyright information */ + buf[4] = 0; /* no copyright data */ + buf[5] = 0; /* no region restrictions */ + + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 4 + 2); + + /* 4 byte header + 4 byte data */ + return (4 + 4); + + case 0x03: /* BCA information - invalid field for no BCA info */ + return -ASC_INV_FIELD_IN_CMD_PACKET; + + case 0x04: /* DVD disc manufacturing information */ + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 2048 + 2); + + /* 2k data + 4 byte header */ + return (2048 + 4); + + case 0xff: + /* + * This lists all the command capabilities above. Add new ones + * in order and update the length and buffer return values. + */ + + buf[4] = 0x00; /* Physical format */ + buf[5] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4); + + buf[8] = 0x01; /* Copyright info */ + buf[9] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4); + + buf[12] = 0x03; /* BCA info */ + buf[13] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4); + + buf[16] = 0x04; /* Manufacturing info */ + buf[17] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4); + + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 16 + 2); + + /* data written + 4 byte header */ + return (16 + 4); + + default: /* TODO: formats beyond DVD-ROM requires */ + return -ASC_INV_FIELD_IN_CMD_PACKET; + } +} + static void ide_atapi_cmd(IDEState *s) { const uint8_t *packet; @@ -1651,42 +1755,48 @@ static void ide_atapi_cmd(IDEState *s) case GPCMD_READ_DVD_STRUCTURE: { int media = packet[1]; - int layer = packet[6]; - int format = packet[2]; - uint64_t total_sectors; - - if (media != 0 || layer != 0) - { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - } - - switch (format) { - case 0: - bdrv_get_geometry(s->bs, &total_sectors); - total_sectors >>= 2; - if (total_sectors == 0) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - break; - } + int format = packet[7]; + int ret; - memset(buf, 0, 2052); + max_len = ube16_to_cpu(packet + 8); - buf[4] = 1; // DVD-ROM, part version 1 - buf[5] = 0xf; // 120mm disc, maximum rate unspecified - buf[6] = 0; // one layer, embossed data - buf[7] = 0; + if (format < 0xff) { + if (media_is_cd(s)) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INCOMPATIBLE_FORMAT); + break; + } else if (!media_present(s)) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + break; + } + } - cpu_to_ube32(buf + 8, 0); - cpu_to_ube32(buf + 12, total_sectors - 1); - cpu_to_ube32(buf + 16, total_sectors - 1); + memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ? + IDE_DMA_BUF_SECTORS * 512 + 4 : max_len); - cpu_to_be16wu((uint16_t *)buf, 2048 + 4); + switch (format) { + case 0x00 ... 0x7f: + case 0xff: + if (media == 0) { + ret = ide_dvd_read_structure(s, format, packet, buf); - ide_atapi_cmd_reply(s, 2048 + 3, 2048 + 4); - break; + if (ret < 0) + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret); + else + ide_atapi_cmd_reply(s, ret, max_len); + break; + } + /* TODO: BD support, fall through for now */ + + /* Generic disk structures */ + case 0x80: /* TODO: AACS volume identifier */ + case 0x81: /* TODO: AACS media serial number */ + case 0x82: /* TODO: AACS media identifier */ + case 0x83: /* TODO: AACS media key block */ + case 0x90: /* TODO: List of recognized format layers */ + case 0xc0: /* TODO: Write protection status */ default: ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); @@ -1741,16 +1851,11 @@ static void ide_atapi_cmd(IDEState *s) /* * the number of sectors from the media tells us which profile * to use as current. 0 means there is no media - * - * XXX: fails to detect correctly DVDs with less data burned - * than what a CD can hold */ - if (s -> nb_sectors) { - if (s -> nb_sectors > CD_MAX_SECTORS) - cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM); - else - cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM); - } + if (media_is_dvd(s)) + cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM); + else if (media_is_cd(s)) + cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM); buf[10] = 0x02 | 0x01; /* persistent and current */ len = 12; /* headers: 8 + 4 */ |