From 13519a81116ee984875cab045e3f1cacc0d22222 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Tue, 19 Apr 2011 15:10:21 -0300 Subject: [PATCH 09/10] atapi: GESN: implement 'media' subcommand RH-Author: Markus Armbruster <armbru@redhat.com> Message-id: <1303225822-22611-8-git-send-email-armbru@redhat.com> Patchwork-id: 22560 O-Subject: [PATCH RHEL5 qemu-kvm 7/8] atapi: GESN: implement 'media' subcommand Bugzilla: 652135 RH-Acked-by: Kevin Wolf <kwolf@redhat.com> RH-Acked-by: Jes Sorensen <Jes.Sorensen@redhat.com> RH-Acked-by: Amit Shah <amit.shah@redhat.com> RH-Acked-by: Alex Williamson <alex.williamson@redhat.com> 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> Manually cherry-picked from qemu commit 996faf1ad4a93342e381766d95686b16624f0dbd Backported from vmstate to register_optional_savevm(). Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qemu/hw/ide.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 139 insertions(+), 4 deletions(-) Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com> --- qemu/hw/ide.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 139 insertions(+), 4 deletions(-) diff --git a/qemu/hw/ide.c b/qemu/hw/ide.c index 033ce62..c68bfae 100644 --- a/qemu/hw/ide.c +++ b/qemu/hw/ide.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include <stdbool.h> #include "hw.h" #include "pc.h" #include "pci.h" @@ -375,6 +376,11 @@ struct IDEState; typedef void EndTransferFunc(struct IDEState *); +struct unreported_events { + bool eject_request; + bool new_media; +}; + /* NOTE: IDEState represents in fact one drive */ typedef struct IDEState { /* ide config */ @@ -415,6 +421,7 @@ typedef struct IDEState { struct IDEState *cur_drive; BlockDriverState *bs; /* ATAPI specific */ + struct unreported_events events; uint8_t sense_key; uint8_t asc; uint8_t cdrom_changed; @@ -1614,6 +1621,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) @@ -1634,6 +1683,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; @@ -1651,12 +1720,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; - gesn_event_header->notification_class = 0x80; /* No event available */ - used_len = sizeof(*gesn_event_header); + /* + * 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); @@ -2165,6 +2254,7 @@ static void cdrom_change_cb(void *opaque) 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); } @@ -3456,6 +3546,48 @@ static int pci_ide_pio_needed(void *opaque) return 0; } +static void pci_ide_atapi_gesn_save(QEMUFile* f, void *opaque, int version_id) +{ + PCIIDEState *d = opaque; + int i; + + for (i = 0; i < 4; i++) { + IDEState *s = &d->ide_if[i]; + + qemu_put_byte(f, s->events.new_media); + qemu_put_byte(f, s->events.eject_request); + } +} + +static int pci_ide_atapi_gesn_load(QEMUFile* f, void *opaque, int version_id) +{ + PCIIDEState *d = opaque; + int i; + + for (i = 0; i < 4; i++) { + IDEState *s = &d->ide_if[i]; + + s->events.new_media = qemu_get_byte(f); + s->events.eject_request = qemu_get_byte(f); + } + return 0; +} + +static int pci_ide_atapi_gesn_needed(void *opaque) +{ + PCIIDEState *d = opaque; + + int i; + + for (i = 0; i < 4; i++) { + IDEState *s = &d->ide_if[i]; + if (s->events.new_media || s->events.eject_request) { + return 1; + } + } + return 0; +} + /* XXX: call it also when the MRDMODE is changed from the PCI config registers */ static void cmd646_update_irq(PCIIDEState *d) @@ -3623,6 +3755,9 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn, * old->new migrations where we could lose inflight pio. */ register_optional_savevm("ide/pio_state", 0, 1, pci_ide_pio_save, pci_ide_pio_load, pci_ide_pio_needed, d); + register_optional_savevm("ide_drive/atapi/gesn_state", 0, 1, + pci_ide_atapi_gesn_save, pci_ide_atapi_gesn_load, + pci_ide_atapi_gesn_needed, d); register_savevm("ide", 0, version_id, pci_ide_save, pci_ide_load, d); } -- 1.7.5.141.g791a