atapi: GESN: implement 'media' subcommand
Implement the 'media' sub-command of the GET_EVENT_STATUS_NOTIFICATION command. This helps us report tray open, tray closed, no media, media present states to the guest. Newer Linux kernels (2.6.38+) rely on this command to revalidate discs after media change. This patch also sends out tray open/closed status to the guest driver when requested e.g. via the CDROM_DRIVE_STATUS ioctl (thanks Markus). Without such notification, the guest and qemu's tray open/close status was frequently out of sync, causing installers like Anaconda detecting no disc instead of tray open, confusing them terribly. Signed-off-by: Amit Shah <amit.shah@redhat.com> Acked-by: Jes Sorensen <Jes.Sorensen@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
0af63ba362
commit
996faf1ad4
111
hw/ide/core.c
111
hw/ide/core.c
|
@ -1084,6 +1084,48 @@ static int ide_dvd_read_structure(IDEState *s, int format,
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int event_status_media(IDEState *s,
|
||||
uint8_t *buf)
|
||||
{
|
||||
enum media_event_code {
|
||||
MEC_NO_CHANGE = 0, /* Status unchanged */
|
||||
MEC_EJECT_REQUESTED, /* received a request from user to eject */
|
||||
MEC_NEW_MEDIA, /* new media inserted and ready for access */
|
||||
MEC_MEDIA_REMOVAL, /* only for media changers */
|
||||
MEC_MEDIA_CHANGED, /* only for media changers */
|
||||
MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */
|
||||
MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */
|
||||
};
|
||||
enum media_status {
|
||||
MS_TRAY_OPEN = 1,
|
||||
MS_MEDIA_PRESENT = 2,
|
||||
};
|
||||
uint8_t event_code, media_status;
|
||||
|
||||
media_status = 0;
|
||||
if (s->bs->tray_open) {
|
||||
media_status = MS_TRAY_OPEN;
|
||||
} else if (bdrv_is_inserted(s->bs)) {
|
||||
media_status = MS_MEDIA_PRESENT;
|
||||
}
|
||||
|
||||
/* Event notification descriptor */
|
||||
event_code = MEC_NO_CHANGE;
|
||||
if (media_status != MS_TRAY_OPEN && s->events.new_media) {
|
||||
event_code = MEC_NEW_MEDIA;
|
||||
s->events.new_media = false;
|
||||
}
|
||||
|
||||
buf[4] = event_code;
|
||||
buf[5] = media_status;
|
||||
|
||||
/* These fields are reserved, just clear them. */
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
return 8; /* We wrote to 4 extra bytes from the header */
|
||||
}
|
||||
|
||||
static void handle_get_event_status_notification(IDEState *s,
|
||||
uint8_t *buf,
|
||||
const uint8_t *packet)
|
||||
|
@ -1104,6 +1146,26 @@ static void handle_get_event_status_notification(IDEState *s,
|
|||
uint8_t supported_events;
|
||||
} __attribute((packed)) *gesn_event_header;
|
||||
|
||||
enum notification_class_request_type {
|
||||
NCR_RESERVED1 = 1 << 0,
|
||||
NCR_OPERATIONAL_CHANGE = 1 << 1,
|
||||
NCR_POWER_MANAGEMENT = 1 << 2,
|
||||
NCR_EXTERNAL_REQUEST = 1 << 3,
|
||||
NCR_MEDIA = 1 << 4,
|
||||
NCR_MULTI_HOST = 1 << 5,
|
||||
NCR_DEVICE_BUSY = 1 << 6,
|
||||
NCR_RESERVED2 = 1 << 7,
|
||||
};
|
||||
enum event_notification_class_field {
|
||||
ENC_NO_EVENTS = 0,
|
||||
ENC_OPERATIONAL_CHANGE,
|
||||
ENC_POWER_MANAGEMENT,
|
||||
ENC_EXTERNAL_REQUEST,
|
||||
ENC_MEDIA,
|
||||
ENC_MULTIPLE_HOSTS,
|
||||
ENC_DEVICE_BUSY,
|
||||
ENC_RESERVED,
|
||||
};
|
||||
unsigned int max_len, used_len;
|
||||
|
||||
gesn_cdb = (void *)packet;
|
||||
|
@ -1121,12 +1183,32 @@ static void handle_get_event_status_notification(IDEState *s,
|
|||
|
||||
/* polling mode operation */
|
||||
|
||||
/* We don't support any event class (yet). */
|
||||
gesn_event_header->supported_events = 0;
|
||||
/*
|
||||
* These are the supported events.
|
||||
*
|
||||
* We currently only support requests of the 'media' type.
|
||||
*/
|
||||
gesn_event_header->supported_events = NCR_MEDIA;
|
||||
|
||||
/*
|
||||
* We use |= below to set the class field; other bits in this byte
|
||||
* are reserved now but this is useful to do if we have to use the
|
||||
* reserved fields later.
|
||||
*/
|
||||
gesn_event_header->notification_class = 0;
|
||||
|
||||
/*
|
||||
* Responses to requests are to be based on request priority. The
|
||||
* notification_class_request_type enum above specifies the
|
||||
* priority: upper elements are higher prio than lower ones.
|
||||
*/
|
||||
if (gesn_cdb->class & NCR_MEDIA) {
|
||||
gesn_event_header->notification_class |= ENC_MEDIA;
|
||||
used_len = event_status_media(s, buf);
|
||||
} else {
|
||||
gesn_event_header->notification_class = 0x80; /* No event available */
|
||||
used_len = sizeof(*gesn_event_header);
|
||||
|
||||
}
|
||||
gesn_event_header->len = cpu_to_be16(used_len
|
||||
- sizeof(*gesn_event_header));
|
||||
ide_atapi_cmd_reply(s, used_len, max_len);
|
||||
|
@ -1655,6 +1737,7 @@ static void cdrom_change_cb(void *opaque, int reason)
|
|||
s->sense_key = SENSE_UNIT_ATTENTION;
|
||||
s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
|
||||
s->cdrom_changed = 1;
|
||||
s->events.new_media = true;
|
||||
ide_set_irq(s->bus);
|
||||
}
|
||||
|
||||
|
@ -2799,6 +2882,25 @@ static bool ide_drive_pio_state_needed(void *opaque)
|
|||
return (s->status & DRQ_STAT) != 0;
|
||||
}
|
||||
|
||||
static bool ide_atapi_gesn_needed(void *opaque)
|
||||
{
|
||||
IDEState *s = opaque;
|
||||
|
||||
return s->events.new_media || s->events.eject_request;
|
||||
}
|
||||
|
||||
/* Fields for GET_EVENT_STATUS_NOTIFICATION ATAPI command */
|
||||
const VMStateDescription vmstate_ide_atapi_gesn_state = {
|
||||
.name ="ide_drive/atapi/gesn_state",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_BOOL(events.new_media, IDEState),
|
||||
VMSTATE_BOOL(events.eject_request, IDEState),
|
||||
}
|
||||
};
|
||||
|
||||
const VMStateDescription vmstate_ide_drive_pio_state = {
|
||||
.name = "ide_drive/pio_state",
|
||||
.version_id = 1,
|
||||
|
@ -2852,6 +2954,9 @@ const VMStateDescription vmstate_ide_drive = {
|
|||
{
|
||||
.vmsd = &vmstate_ide_drive_pio_state,
|
||||
.needed = ide_drive_pio_state_needed,
|
||||
}, {
|
||||
.vmsd = &vmstate_ide_atapi_gesn_state,
|
||||
.needed = ide_atapi_gesn_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
|
|
|
@ -373,6 +373,11 @@ typedef int DMAFunc(IDEDMA *);
|
|||
typedef int DMAIntFunc(IDEDMA *, int);
|
||||
typedef void DMARestartFunc(void *, int, int);
|
||||
|
||||
struct unreported_events {
|
||||
bool eject_request;
|
||||
bool new_media;
|
||||
};
|
||||
|
||||
/* NOTE: IDEState represents in fact one drive */
|
||||
struct IDEState {
|
||||
IDEBus *bus;
|
||||
|
@ -408,6 +413,7 @@ struct IDEState {
|
|||
BlockDriverState *bs;
|
||||
char version[9];
|
||||
/* ATAPI specific */
|
||||
struct unreported_events events;
|
||||
uint8_t sense_key;
|
||||
uint8_t asc;
|
||||
uint8_t cdrom_changed;
|
||||
|
|
Loading…
Reference in a new issue