ide/atapi: Factor commands out

In preparation for a table of function pointers, factor each command out from
ide_atapi_cmd() into its own function.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Kevin Wolf 2011-04-18 17:55:08 +02:00
parent 33231e0e22
commit a60cf7e7eb
1 changed files with 459 additions and 378 deletions

View File

@ -621,67 +621,103 @@ static void handle_get_event_status_notification(IDEState *s,
ide_atapi_cmd_reply(s, used_len, max_len); ide_atapi_cmd_reply(s, used_len, max_len);
} }
void ide_atapi_cmd(IDEState *s) static void cmd_request_sense(IDEState *s, uint8_t *buf)
{ {
const uint8_t *packet; int max_len = buf[4];
uint8_t *buf;
memset(buf, 0, 18);
buf[0] = 0x70 | (1 << 7);
buf[2] = s->sense_key;
buf[7] = 10;
buf[12] = s->asc;
if (s->sense_key == SENSE_UNIT_ATTENTION) {
s->sense_key = SENSE_NONE;
}
ide_atapi_cmd_reply(s, 18, max_len);
}
static void cmd_inquiry(IDEState *s, uint8_t *buf)
{
int max_len = buf[4];
buf[0] = 0x05; /* CD-ROM */
buf[1] = 0x80; /* removable */
buf[2] = 0x00; /* ISO */
buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
buf[4] = 31; /* additional length */
buf[5] = 0; /* reserved */
buf[6] = 0; /* reserved */
buf[7] = 0; /* reserved */
padstr8(buf + 8, 8, "QEMU");
padstr8(buf + 16, 16, "QEMU DVD-ROM");
padstr8(buf + 32, 4, s->version);
ide_atapi_cmd_reply(s, 36, max_len);
}
static void cmd_get_configuration(IDEState *s, uint8_t *buf)
{
uint32_t len;
uint8_t index = 0;
int max_len; int max_len;
packet = s->io_buffer; /* only feature 0 is supported */
buf = s->io_buffer; if (buf[2] != 0 || buf[3] != 0) {
#ifdef DEBUG_IDE_ATAPI ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
{ ASC_INV_FIELD_IN_CMD_PACKET);
int i;
printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
printf(" %02x", packet[i]);
}
printf("\n");
}
#endif
/*
* If there's a UNIT_ATTENTION condition pending, only
* REQUEST_SENSE, INQUIRY, GET_CONFIGURATION and
* GET_EVENT_STATUS_NOTIFICATION commands are allowed to complete.
* MMC-5, section 4.1.6.1 lists only these commands being allowed
* to complete, with other commands getting a CHECK condition
* response unless a higher priority status, defined by the drive
* here, is pending.
*/
if (s->sense_key == SENSE_UNIT_ATTENTION &&
s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
s->io_buffer[0] != GPCMD_INQUIRY &&
s->io_buffer[0] != GPCMD_GET_EVENT_STATUS_NOTIFICATION) {
ide_atapi_cmd_check_status(s);
return; return;
} }
if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
s->cdrom_changed = 0; /* XXX: could result in alignment problems in some architectures */
s->sense_key = SENSE_UNIT_ATTENTION; max_len = ube16_to_cpu(buf + 7);
s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
return; /*
* XXX: avoid overflow for io_buffer if max_len is bigger than
* the size of that buffer (dimensioned to max number of
* sectors to transfer at once)
*
* Only a problem if the feature/profiles grow.
*/
if (max_len > 512) {
/* XXX: assume 1 sector */
max_len = 512;
} }
switch(s->io_buffer[0]) {
case GPCMD_TEST_UNIT_READY: memset(buf, 0, max_len);
if (bdrv_is_inserted(s->bs)) { /*
ide_atapi_cmd_ok(s); * the number of sectors from the media tells us which profile
} else { * to use as current. 0 means there is no media
ide_atapi_cmd_error(s, SENSE_NOT_READY, */
ASC_MEDIUM_NOT_PRESENT); 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);
} }
break;
case GPCMD_MODE_SENSE_6: buf[10] = 0x02 | 0x01; /* persistent and current */
case GPCMD_MODE_SENSE_10: len = 12; /* headers: 8 + 4 */
len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
cpu_to_ube32(buf, len - 4); /* data length */
ide_atapi_cmd_reply(s, len, max_len);
}
static void cmd_mode_sense(IDEState *s, uint8_t *buf)
{ {
int action, code; int action, code;
if (packet[0] == GPCMD_MODE_SENSE_10) int max_len;
max_len = ube16_to_cpu(packet + 7);
else if (buf[0] == GPCMD_MODE_SENSE_10) {
max_len = packet[4]; max_len = ube16_to_cpu(buf + 7);
action = packet[2] >> 6; } else {
code = packet[2] & 0x3f; max_len = buf[4];
}
action = buf[2] >> 6;
code = buf[2] & 0x3f;
switch(action) { switch(action) {
case 0: /* current values */ case 0: /* current values */
switch(code) { switch(code) {
@ -768,51 +804,59 @@ void ide_atapi_cmd(IDEState *s)
ASC_SAVING_PARAMETERS_NOT_SUPPORTED); ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
break; break;
} }
return;
error_cmd:
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
} }
break;
case GPCMD_REQUEST_SENSE: static void cmd_test_unit_ready(IDEState *s, uint8_t *buf)
max_len = packet[4]; {
memset(buf, 0, 18); if (bdrv_is_inserted(s->bs)) {
buf[0] = 0x70 | (1 << 7);
buf[2] = s->sense_key;
buf[7] = 10;
buf[12] = s->asc;
if (s->sense_key == SENSE_UNIT_ATTENTION)
s->sense_key = SENSE_NONE;
ide_atapi_cmd_reply(s, 18, max_len);
break;
case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
bdrv_set_locked(s->bs, packet[4] & 1);
ide_atapi_cmd_ok(s); ide_atapi_cmd_ok(s);
break; } else {
case GPCMD_READ_10: ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
case GPCMD_READ_12: }
}
static void cmd_prevent_allow_medium_removal(IDEState *s, uint8_t* buf)
{
bdrv_set_locked(s->bs, buf[4] & 1);
ide_atapi_cmd_ok(s);
}
static void cmd_read(IDEState *s, uint8_t* buf)
{ {
int nb_sectors, lba; int nb_sectors, lba;
if (packet[0] == GPCMD_READ_10) if (buf[0] == GPCMD_READ_10) {
nb_sectors = ube16_to_cpu(packet + 7); nb_sectors = ube16_to_cpu(buf + 7);
else } else {
nb_sectors = ube32_to_cpu(packet + 6); nb_sectors = ube32_to_cpu(buf + 6);
lba = ube32_to_cpu(packet + 2); }
lba = ube32_to_cpu(buf + 2);
if (nb_sectors == 0) { if (nb_sectors == 0) {
ide_atapi_cmd_ok(s); ide_atapi_cmd_ok(s);
break; return;
} }
ide_atapi_cmd_read(s, lba, nb_sectors, 2048); ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
} }
break;
case GPCMD_READ_CD: static void cmd_read_cd(IDEState *s, uint8_t* buf)
{ {
int nb_sectors, lba, transfer_request; int nb_sectors, lba, transfer_request;
nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8]; nb_sectors = (buf[6] << 16) | (buf[7] << 8) | buf[8];
lba = ube32_to_cpu(packet + 2); lba = ube32_to_cpu(buf + 2);
if (nb_sectors == 0) { if (nb_sectors == 0) {
ide_atapi_cmd_ok(s); ide_atapi_cmd_ok(s);
break; return;
} }
transfer_request = packet[9];
transfer_request = buf[9];
switch(transfer_request & 0xf8) { switch(transfer_request & 0xf8) {
case 0x00: case 0x00:
/* nothing */ /* nothing */
@ -832,33 +876,34 @@ void ide_atapi_cmd(IDEState *s)
break; break;
} }
} }
break;
case GPCMD_SEEK: static void cmd_seek(IDEState *s, uint8_t* buf)
{ {
unsigned int lba; unsigned int lba;
uint64_t total_sectors; uint64_t total_sectors;
bdrv_get_geometry(s->bs, &total_sectors); bdrv_get_geometry(s->bs, &total_sectors);
total_sectors >>= 2; total_sectors >>= 2;
if (total_sectors == 0) { if (total_sectors == 0) {
ide_atapi_cmd_error(s, SENSE_NOT_READY, ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
ASC_MEDIUM_NOT_PRESENT); return;
break;
} }
lba = ube32_to_cpu(packet + 2);
lba = ube32_to_cpu(buf + 2);
if (lba >= total_sectors) { if (lba >= total_sectors) {
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
ASC_LOGICAL_BLOCK_OOR); return;
break;
} }
ide_atapi_cmd_ok(s); ide_atapi_cmd_ok(s);
} }
break;
case GPCMD_START_STOP_UNIT: static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
{ {
int start, eject, sense, err = 0; int start, eject, sense, err = 0;
start = packet[4] & 1; start = buf[4] & 1;
eject = (packet[4] >> 1) & 1; eject = (buf[4] >> 1) & 1;
if (eject) { if (eject) {
err = bdrv_eject(s->bs, !start); err = bdrv_eject(s->bs, !start);
@ -873,19 +918,18 @@ void ide_atapi_cmd(IDEState *s)
if (bdrv_is_inserted(s->bs)) { if (bdrv_is_inserted(s->bs)) {
sense = SENSE_ILLEGAL_REQUEST; sense = SENSE_ILLEGAL_REQUEST;
} }
ide_atapi_cmd_error(s, sense, ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
ASC_MEDIA_REMOVAL_PREVENTED);
break; break;
default: default:
ide_atapi_cmd_error(s, SENSE_NOT_READY, ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
ASC_MEDIUM_NOT_PRESENT);
break; break;
} }
} }
break;
case GPCMD_MECHANISM_STATUS: static void cmd_mechanism_status(IDEState *s, uint8_t* buf)
{ {
max_len = ube16_to_cpu(packet + 8); int max_len = ube16_to_cpu(buf + 8);
cpu_to_ube16(buf, 0); cpu_to_ube16(buf, 0);
/* no current LBA */ /* no current LBA */
buf[2] = 0; buf[2] = 0;
@ -895,23 +939,26 @@ void ide_atapi_cmd(IDEState *s)
cpu_to_ube16(buf + 6, 0); cpu_to_ube16(buf + 6, 0);
ide_atapi_cmd_reply(s, 8, max_len); ide_atapi_cmd_reply(s, 8, max_len);
} }
break;
case GPCMD_READ_TOC_PMA_ATIP: static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
{ {
int format, msf, start_track, len; int format, msf, start_track, len;
uint64_t total_sectors; uint64_t total_sectors;
int max_len;
bdrv_get_geometry(s->bs, &total_sectors); bdrv_get_geometry(s->bs, &total_sectors);
total_sectors >>= 2; total_sectors >>= 2;
if (total_sectors == 0) { if (total_sectors == 0) {
ide_atapi_cmd_error(s, SENSE_NOT_READY, ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
ASC_MEDIUM_NOT_PRESENT); return;
break;
} }
max_len = ube16_to_cpu(packet + 7);
format = packet[9] >> 6; max_len = ube16_to_cpu(buf + 7);
msf = (packet[1] >> 1) & 1; format = buf[9] >> 6;
start_track = packet[6]; msf = (buf[1] >> 1) & 1;
start_track = buf[6];
switch(format) { switch(format) {
case 0: case 0:
len = cdrom_read_toc(total_sectors, buf, msf, start_track); len = cdrom_read_toc(total_sectors, buf, msf, start_track);
@ -937,44 +984,45 @@ void ide_atapi_cmd(IDEState *s)
error_cmd: error_cmd:
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET); ASC_INV_FIELD_IN_CMD_PACKET);
break;
} }
} }
break;
case GPCMD_READ_CDVD_CAPACITY: static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf)
{ {
uint64_t total_sectors; uint64_t total_sectors;
bdrv_get_geometry(s->bs, &total_sectors); bdrv_get_geometry(s->bs, &total_sectors);
total_sectors >>= 2; total_sectors >>= 2;
if (total_sectors == 0) { if (total_sectors == 0) {
ide_atapi_cmd_error(s, SENSE_NOT_READY, ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
ASC_MEDIUM_NOT_PRESENT); return;
break;
} }
/* NOTE: it is really the number of sectors minus 1 */ /* NOTE: it is really the number of sectors minus 1 */
cpu_to_ube32(buf, total_sectors - 1); cpu_to_ube32(buf, total_sectors - 1);
cpu_to_ube32(buf + 4, 2048); cpu_to_ube32(buf + 4, 2048);
ide_atapi_cmd_reply(s, 8, 8); ide_atapi_cmd_reply(s, 8, 8);
} }
break;
case GPCMD_READ_DVD_STRUCTURE: static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
{ {
int media = packet[1]; int max_len;
int format = packet[7]; int media = buf[1];
int format = buf[7];
int ret; int ret;
max_len = ube16_to_cpu(packet + 8); max_len = ube16_to_cpu(buf + 8);
if (format < 0xff) { if (format < 0xff) {
if (media_is_cd(s)) { if (media_is_cd(s)) {
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
ASC_INCOMPATIBLE_FORMAT); ASC_INCOMPATIBLE_FORMAT);
break; return;
} else if (!media_present(s)) { } else if (!media_present(s)) {
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET); ASC_INV_FIELD_IN_CMD_PACKET);
break; return;
} }
} }
@ -985,12 +1033,13 @@ void ide_atapi_cmd(IDEState *s)
case 0x00 ... 0x7f: case 0x00 ... 0x7f:
case 0xff: case 0xff:
if (media == 0) { if (media == 0) {
ret = ide_dvd_read_structure(s, format, packet, buf); ret = ide_dvd_read_structure(s, format, buf, buf);
if (ret < 0) if (ret < 0) {
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret); ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
else } else {
ide_atapi_cmd_reply(s, ret, max_len); ide_atapi_cmd_reply(s, ret, max_len);
}
break; break;
} }
@ -1009,69 +1058,101 @@ void ide_atapi_cmd(IDEState *s)
break; break;
} }
} }
static void cmd_set_speed(IDEState *s, uint8_t* buf)
{
ide_atapi_cmd_ok(s);
}
void ide_atapi_cmd(IDEState *s)
{
const uint8_t *packet;
uint8_t *buf;
packet = s->io_buffer;
buf = s->io_buffer;
#ifdef DEBUG_IDE_ATAPI
{
int i;
printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
printf(" %02x", packet[i]);
}
printf("\n");
}
#endif
/*
* If there's a UNIT_ATTENTION condition pending, only
* REQUEST_SENSE, INQUIRY, GET_CONFIGURATION and
* GET_EVENT_STATUS_NOTIFICATION commands are allowed to complete.
* MMC-5, section 4.1.6.1 lists only these commands being allowed
* to complete, with other commands getting a CHECK condition
* response unless a higher priority status, defined by the drive
* here, is pending.
*/
if (s->sense_key == SENSE_UNIT_ATTENTION &&
s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
s->io_buffer[0] != GPCMD_INQUIRY &&
s->io_buffer[0] != GPCMD_GET_EVENT_STATUS_NOTIFICATION) {
ide_atapi_cmd_check_status(s);
return;
}
if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
s->cdrom_changed = 0;
s->sense_key = SENSE_UNIT_ATTENTION;
s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
return;
}
switch(s->io_buffer[0]) {
case GPCMD_TEST_UNIT_READY:
cmd_test_unit_ready(s, buf);
break;
case GPCMD_MODE_SENSE_6:
case GPCMD_MODE_SENSE_10:
cmd_mode_sense(s, buf);
break;
case GPCMD_REQUEST_SENSE:
cmd_request_sense(s, buf);
break;
case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
cmd_prevent_allow_medium_removal(s, buf);
break;
case GPCMD_READ_10:
case GPCMD_READ_12:
cmd_read(s, buf);
break;
case GPCMD_READ_CD:
cmd_read_cd(s, buf);
break;
case GPCMD_SEEK:
cmd_seek(s, buf);
break;
case GPCMD_START_STOP_UNIT:
cmd_start_stop_unit(s, buf);
break;
case GPCMD_MECHANISM_STATUS:
cmd_mechanism_status(s, buf);
break;
case GPCMD_READ_TOC_PMA_ATIP:
cmd_read_toc_pma_atip(s, buf);
break;
case GPCMD_READ_CDVD_CAPACITY:
cmd_read_cdvd_capacity(s, buf);
break;
case GPCMD_READ_DVD_STRUCTURE:
cmd_read_dvd_structure(s, buf);
break; break;
case GPCMD_SET_SPEED: case GPCMD_SET_SPEED:
ide_atapi_cmd_ok(s); cmd_set_speed(s, buf);
break; break;
case GPCMD_INQUIRY: case GPCMD_INQUIRY:
max_len = packet[4]; cmd_inquiry(s, buf);
buf[0] = 0x05; /* CD-ROM */
buf[1] = 0x80; /* removable */
buf[2] = 0x00; /* ISO */
buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
buf[4] = 31; /* additional length */
buf[5] = 0; /* reserved */
buf[6] = 0; /* reserved */
buf[7] = 0; /* reserved */
padstr8(buf + 8, 8, "QEMU");
padstr8(buf + 16, 16, "QEMU DVD-ROM");
padstr8(buf + 32, 4, s->version);
ide_atapi_cmd_reply(s, 36, max_len);
break; break;
case GPCMD_GET_CONFIGURATION: case GPCMD_GET_CONFIGURATION:
{ cmd_get_configuration(s, buf);
uint32_t len;
uint8_t index = 0;
/* only feature 0 is supported */
if (packet[2] != 0 || packet[3] != 0) {
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
break; break;
}
/* XXX: could result in alignment problems in some architectures */
max_len = ube16_to_cpu(packet + 7);
/*
* XXX: avoid overflow for io_buffer if max_len is bigger than
* the size of that buffer (dimensioned to max number of
* sectors to transfer at once)
*
* Only a problem if the feature/profiles grow.
*/
if (max_len > 512) /* XXX: assume 1 sector */
max_len = 512;
memset(buf, 0, max_len);
/*
* the number of sectors from the media tells us which profile
* to use as current. 0 means there is no media
*/
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 */
len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
cpu_to_ube32(buf, len - 4); /* data length */
ide_atapi_cmd_reply(s, len, max_len);
break;
}
case GPCMD_GET_EVENT_STATUS_NOTIFICATION: case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
handle_get_event_status_notification(s, buf, packet); handle_get_event_status_notification(s, buf, packet);
break; break;