Blob Blame History Raw
From: plc@novell.com
Subject: implement forwarding of CD-ROM specific commands
Patch-mainline: obsolete
References: fate#300964

--- head.orig/drivers/cdrom/Makefile	2012-06-12 15:05:48.000000000 +0200
+++ head/drivers/cdrom/Makefile	2010-11-23 15:06:54.000000000 +0100
@@ -9,6 +9,7 @@ obj-$(CONFIG_BLK_DEV_IDECD)	+=          
 obj-$(CONFIG_BLK_DEV_SR)	+=              cdrom.o
 obj-$(CONFIG_PARIDE_PCD)	+=		cdrom.o
 obj-$(CONFIG_CDROM_PKTCDVD)	+=		cdrom.o
+obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+=		cdrom.o
 
 obj-$(CONFIG_VIOCD)		+= viocd.o      cdrom.o
 obj-$(CONFIG_GDROM)		+= gdrom.o      cdrom.o
--- head.orig/drivers/xen/blkfront/Makefile	2007-06-12 13:13:44.000000000 +0200
+++ head/drivers/xen/blkfront/Makefile	2010-11-23 15:06:54.000000000 +0100
@@ -1,5 +1,5 @@
 
 obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	:= xenblk.o
 
-xenblk-objs := blkfront.o vbd.o
+xenblk-objs := blkfront.o vbd.o vcd.o
 
--- head.orig/drivers/xen/blkfront/blkfront.c	2012-06-12 15:39:16.000000000 +0200
+++ head/drivers/xen/blkfront/blkfront.c	2012-06-12 15:39:55.000000000 +0200
@@ -379,6 +379,13 @@ static void connect(struct blkfront_info
 				   "sectors", "%Lu", &sectors);
 		if (err != 1)
 			return;
+		err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+				   "sector-size", "%lu", &sector_size);
+		if (err != 1)
+			sector_size = 0;
+		if (sector_size)
+			blk_queue_logical_block_size(info->gd->queue,
+						     sector_size);
 		pr_info("Setting capacity to %Lu\n", sectors);
 		set_capacity(info->gd, sectors);
 		revalidate_disk(info->gd);
@@ -469,6 +476,8 @@ static void connect(struct blkfront_info
 	add_disk(info->gd);
 
 	info->is_ready = 1;
+
+	register_vcd(info);
 }
 
 /**
@@ -498,6 +507,8 @@ static void blkfront_closing(struct blkf
 
 	xlvbd_sysfs_delif(info);
 
+	unregister_vcd(info);
+
 	xlvbd_del(info);
 
  out:
--- head.orig/drivers/xen/blkfront/block.h	2012-06-08 10:38:23.000000000 +0200
+++ head/drivers/xen/blkfront/block.h	2012-06-08 10:40:36.000000000 +0200
@@ -158,4 +158,8 @@ static inline void xlvbd_sysfs_delif(str
 
 void xlbd_release_major_info(void);
 
+/* Virtual cdrom block-device */
+extern void register_vcd(struct blkfront_info *info);
+extern void unregister_vcd(struct blkfront_info *info);
+
 #endif /* __XEN_DRIVERS_BLOCK_H__ */
--- head.orig/drivers/xen/blkfront/vbd.c	2012-03-12 16:19:08.000000000 +0100
+++ head/drivers/xen/blkfront/vbd.c	2012-03-12 16:19:39.000000000 +0100
@@ -439,15 +439,16 @@ xlvbd_add(blkif_sector_t capacity, int v
 		goto out;
 	info->mi = mi;
 
-	if ((minor & ((1 << mi->type->partn_shift) - 1)) == 0)
+	if ((vdisk_info & VDISK_CDROM) ||
+	    !(minor & ((1 << mi->type->partn_shift) - 1)))
 		nr_minors = 1 << mi->type->partn_shift;
 
-	err = xlbd_reserve_minors(mi, minor, nr_minors);
+	err = xlbd_reserve_minors(mi, minor & ~(nr_minors - 1), nr_minors);
 	if (err)
 		goto out;
 	err = -ENODEV;
 
-	gd = alloc_disk(nr_minors);
+	gd = alloc_disk(vdisk_info & VDISK_CDROM ? 1 : nr_minors);
 	if (gd == NULL)
 		goto release;
 
@@ -513,12 +514,14 @@ xlvbd_del(struct blkfront_info *info)
 
 	BUG_ON(info->gd == NULL);
 	minor = info->gd->first_minor;
-	nr_minors = info->gd->minors;
+	nr_minors = (info->gd->flags & GENHD_FL_CD)
+		    || !(minor & ((1 << info->mi->type->partn_shift) - 1))
+		    ? 1 << info->mi->type->partn_shift : 1;
 	del_gendisk(info->gd);
 	put_disk(info->gd);
 	info->gd = NULL;
 
-	xlbd_release_minors(info->mi, minor, nr_minors);
+	xlbd_release_minors(info->mi, minor & ~(nr_minors - 1), nr_minors);
 	xlbd_put_major_info(info->mi);
 	info->mi = NULL;
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ head/drivers/xen/blkfront/vcd.c	2012-03-05 11:38:22.000000000 +0100
@@ -0,0 +1,494 @@
+/*******************************************************************************
+ * vcd.c
+ *
+ * Implements CDROM cmd packet passing between frontend guest and backend driver.
+ *
+ * Copyright (c) 2008, Pat Campell  plc@novell.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/list.h>
+#include <linux/cdrom.h>
+#include <xen/interface/io/cdromif.h>
+#include "block.h"
+
+/* List of cdrom_device_info, can have as many as blkfront supports */
+struct vcd_disk {
+	struct list_head vcd_entry;
+	struct cdrom_device_info vcd_cdrom_info;
+	spinlock_t vcd_cdrom_info_lock;
+};
+static LIST_HEAD(vcd_disks);
+static DEFINE_SPINLOCK(vcd_disks_lock);
+
+static struct vcd_disk *xencdrom_get_list_entry(struct gendisk *disk)
+{
+	struct vcd_disk *ret_vcd = NULL;
+	struct vcd_disk *vcd;
+
+	spin_lock(&vcd_disks_lock);
+	list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
+		if (vcd->vcd_cdrom_info.disk == disk) {
+			spin_lock(&vcd->vcd_cdrom_info_lock);
+			ret_vcd = vcd;
+			break;
+		}
+	}
+	spin_unlock(&vcd_disks_lock);
+	return ret_vcd;
+}
+
+static void submit_message(struct blkfront_info *info, void *sp)
+{
+	struct request *req = NULL;
+
+	req = blk_get_request(info->rq, READ, __GFP_WAIT);
+	if (blk_rq_map_kern(info->rq, req, sp, PAGE_SIZE, __GFP_WAIT))
+		goto out;
+
+	req->rq_disk = info->gd;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
+	req->cmd_type = REQ_TYPE_BLOCK_PC;
+	req->cmd_flags |= REQ_NOMERGE;
+#else
+	req->flags |= REQ_BLOCK_PC;
+#endif
+	req->__sector = 0;
+	req->cmd_len = 0;
+	req->timeout = 60*HZ;
+
+	blk_execute_rq(req->q, info->gd, req, 1);
+
+out:
+	blk_put_request(req);
+}
+
+static int submit_cdrom_cmd(struct blkfront_info *info,
+			    struct packet_command *cgc)
+{
+	int ret = 0;
+	struct page *page;
+	union xen_block_packet *sp;
+	struct xen_cdrom_packet *xcp;
+	struct vcd_generic_command *vgc;
+
+	if (cgc->buffer && cgc->buflen > MAX_PACKET_DATA) {
+		pr_warn("%s() Packet buffer length is to large \n", __func__);
+		return -EIO;
+	}
+
+	page = alloc_page(GFP_NOIO|__GFP_ZERO);
+	if (!page) {
+		pr_crit("%s() Unable to allocate page\n", __func__);
+		return -ENOMEM;
+	}
+
+	sp = page_address(page);
+	xcp = &(sp->xcp);
+	xcp->type = XEN_TYPE_CDROM_PACKET;
+	xcp->payload_offset = PACKET_PAYLOAD_OFFSET;
+
+	vgc = (struct vcd_generic_command *)((char *)sp + xcp->payload_offset);
+	memcpy(vgc->cmd, cgc->cmd, CDROM_PACKET_SIZE);
+	vgc->stat = cgc->stat;
+	vgc->data_direction = cgc->data_direction;
+	vgc->quiet = cgc->quiet;
+	vgc->timeout = cgc->timeout;
+	if (cgc->sense) {
+		vgc->sense_offset = PACKET_SENSE_OFFSET;
+		memcpy((char *)sp + vgc->sense_offset, cgc->sense, sizeof(struct request_sense));
+	}
+	if (cgc->buffer) {
+		vgc->buffer_offset = PACKET_BUFFER_OFFSET;
+		memcpy((char *)sp + vgc->buffer_offset, cgc->buffer, cgc->buflen);
+		vgc->buflen = cgc->buflen;
+	}
+
+	submit_message(info,sp);
+
+	if (xcp->ret)
+		ret = xcp->err;
+
+	if (cgc->sense)
+		memcpy(cgc->sense, (char *)sp + PACKET_SENSE_OFFSET, sizeof(struct request_sense));
+	if (cgc->buffer && cgc->buflen)
+		memcpy(cgc->buffer, (char *)sp + PACKET_BUFFER_OFFSET, cgc->buflen);
+
+	__free_page(page);
+	return ret;
+}
+
+
+static int xencdrom_open(struct cdrom_device_info *cdi, int purpose)
+{
+	int ret = 0;
+	struct page *page;
+	struct blkfront_info *info;
+	union xen_block_packet *sp;
+	struct xen_cdrom_open *xco;
+
+	info = cdi->disk->private_data;
+
+	if (!info->xbdev)
+		return -ENODEV;
+
+	if (strlen(info->xbdev->otherend) > MAX_PACKET_DATA) {
+		return -EIO;
+	}
+
+	page = alloc_page(GFP_NOIO|__GFP_ZERO);
+	if (!page) {
+		pr_crit("%s() Unable to allocate page\n", __func__);
+		return -ENOMEM;
+	}
+
+	sp = page_address(page);
+	xco = &(sp->xco);
+	xco->type = XEN_TYPE_CDROM_OPEN;
+	xco->payload_offset = sizeof(struct xen_cdrom_open);
+	strcpy((char *)sp + xco->payload_offset, info->xbdev->otherend);
+
+	submit_message(info,sp);
+
+	if (xco->ret) {
+		ret = xco->err;
+		goto out;
+	}
+
+	if (xco->media_present)
+		set_capacity(cdi->disk, xco->sectors);
+
+out:
+	__free_page(page);
+	return ret;
+}
+
+static void xencdrom_release(struct cdrom_device_info *cdi)
+{
+}
+
+static int xencdrom_media_changed(struct cdrom_device_info *cdi, int disc_nr)
+{
+	int ret;
+	struct page *page;
+	struct blkfront_info *info;
+	union xen_block_packet *sp;
+	struct xen_cdrom_media_changed *xcmc;
+
+	info = cdi->disk->private_data;
+
+	page = alloc_page(GFP_NOIO|__GFP_ZERO);
+	if (!page) {
+		pr_crit("%s() Unable to allocate page\n", __func__);
+		return -ENOMEM;
+	}
+
+	sp = page_address(page);
+	xcmc = &(sp->xcmc);
+	xcmc->type = XEN_TYPE_CDROM_MEDIA_CHANGED;
+	submit_message(info,sp);
+	ret = xcmc->media_changed;
+
+	__free_page(page);
+
+	return ret;
+}
+
+static int xencdrom_tray_move(struct cdrom_device_info *cdi, int position)
+{
+	struct packet_command cgc;
+	struct blkfront_info *info;
+
+	info = cdi->disk->private_data;
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_START_STOP_UNIT;
+	if (position)
+		cgc.cmd[4] = 2;
+	else
+		cgc.cmd[4] = 3;
+
+	return submit_cdrom_cmd(info, &cgc);
+}
+
+static int xencdrom_lock_door(struct cdrom_device_info *cdi, int lock)
+{
+	struct blkfront_info *info;
+	struct packet_command cgc;
+
+	info = cdi->disk->private_data;
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+	cgc.cmd[4] = lock;
+
+	return submit_cdrom_cmd(info, &cgc);
+}
+
+static int xencdrom_packet(struct cdrom_device_info *cdi,
+			   struct packet_command *cgc)
+{
+	return cgc->stat = submit_cdrom_cmd(cdi->disk->private_data, cgc);
+}
+
+static int xencdrom_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
+		void *arg)
+{
+	return -EINVAL;
+}
+
+/* Query backend to see if CDROM packets are supported */
+static int xencdrom_supported(struct blkfront_info *info)
+{
+	struct page *page;
+	union xen_block_packet *sp;
+	struct xen_cdrom_support *xcs;
+
+	page = alloc_page(GFP_NOIO|__GFP_ZERO);
+	if (!page) {
+		pr_crit("%s() Unable to allocate page\n", __func__);
+		return -ENOMEM;
+	}
+
+	sp = page_address(page);
+	xcs = &(sp->xcs);
+	xcs->type = XEN_TYPE_CDROM_SUPPORT;
+	submit_message(info,sp);
+	return xcs->supported;
+}
+
+static struct cdrom_device_ops xencdrom_dops = {
+    .open           = xencdrom_open,
+    .release        = xencdrom_release,
+    .media_changed  = xencdrom_media_changed,
+    .tray_move      = xencdrom_tray_move,
+    .lock_door      = xencdrom_lock_door,
+    .generic_packet = xencdrom_packet,
+    .audio_ioctl    = xencdrom_audio_ioctl,
+    .capability     = (CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | \
+                       CDC_MEDIA_CHANGED | CDC_GENERIC_PACKET |  CDC_DVD | \
+                       CDC_CD_R),
+    .n_minors       = 1,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int xencdrom_block_open(struct inode *inode, struct file *file)
+{
+	struct block_device *bd = inode->i_bdev;
+#else
+static int xencdrom_block_open(struct block_device *bd, fmode_t mode)
+{
+#endif
+	struct blkfront_info *info = bd->bd_disk->private_data;
+	struct vcd_disk *vcd;
+	int ret = 0;
+
+	if (!info->xbdev)
+		return -ENODEV;
+
+	if ((vcd = xencdrom_get_list_entry(info->gd))) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		ret = cdrom_open(&vcd->vcd_cdrom_info, inode, file);
+#else
+		ret = cdrom_open(&vcd->vcd_cdrom_info, bd, mode);
+#endif
+		spin_unlock(&vcd->vcd_cdrom_info_lock);
+	}
+
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int xencdrom_block_release(struct inode *inode, struct file *file)
+{
+	struct gendisk *gd = inode->i_bdev->bd_disk;
+#else
+static int xencdrom_block_release(struct gendisk *gd, fmode_t mode)
+{
+#endif
+	struct blkfront_info *info = gd->private_data;
+	struct vcd_disk *vcd;
+	int ret = 0;
+
+	if ((vcd = xencdrom_get_list_entry(info->gd))) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		ret = cdrom_release(&vcd->vcd_cdrom_info, file);
+#else
+		cdrom_release(&vcd->vcd_cdrom_info, mode);
+#endif
+		spin_unlock(&vcd->vcd_cdrom_info_lock);
+		if (vcd->vcd_cdrom_info.use_count == 0) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+			blkif_release(inode, file);
+#else
+			blkif_release(gd, mode);
+#endif
+		}
+	}
+
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int xencdrom_block_ioctl(struct inode *inode, struct file *file,
+				unsigned cmd, unsigned long arg)
+{
+	struct block_device *bd = inode->i_bdev;
+#else
+static int xencdrom_block_ioctl(struct block_device *bd, fmode_t mode,
+				unsigned cmd, unsigned long arg)
+{
+#endif
+	struct blkfront_info *info = bd->bd_disk->private_data;
+	struct vcd_disk *vcd;
+	int ret = 0;
+
+	if (!(vcd = xencdrom_get_list_entry(info->gd)))
+		goto out;
+
+	switch (cmd) {
+	case 2285: /* SG_IO */
+		ret = -ENOSYS;
+		break;
+	case CDROMEJECT:
+		ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 1);
+		break;
+	case CDROMCLOSETRAY:
+		ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 0);
+		break;
+	case CDROM_GET_CAPABILITY:
+		ret = vcd->vcd_cdrom_info.ops->capability & ~vcd->vcd_cdrom_info.mask;
+		break;
+	case CDROM_SET_OPTIONS:
+		ret = vcd->vcd_cdrom_info.options;
+		break;
+	case CDROM_SEND_PACKET: {
+		struct packet_command cgc;
+
+		ret = copy_from_user(&cgc, (void __user *)arg, sizeof(cgc))
+		      ? -EFAULT : submit_cdrom_cmd(info, &cgc);
+		break;
+	}
+	default:
+		spin_unlock(&vcd->vcd_cdrom_info_lock);
+out:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		return blkif_ioctl(inode, file, cmd, arg);
+#else
+		return blkif_ioctl(bd, mode, cmd, arg);
+#endif
+	}
+	spin_unlock(&vcd->vcd_cdrom_info_lock);
+
+	return ret;
+}
+
+/* Called as result of cdrom_open, vcd_cdrom_info_lock already held */
+static int xencdrom_block_media_changed(struct gendisk *disk)
+{
+	struct vcd_disk *vcd;
+	struct vcd_disk *ret_vcd = NULL;
+
+	spin_lock(&vcd_disks_lock);
+	list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
+		if (vcd->vcd_cdrom_info.disk == disk) {
+			ret_vcd = vcd;
+			break;
+		}
+	}
+	spin_unlock(&vcd_disks_lock);
+
+	return ret_vcd ? cdrom_media_changed(&ret_vcd->vcd_cdrom_info) : 0;
+}
+
+static const struct block_device_operations xencdrom_bdops =
+{
+	.owner		= THIS_MODULE,
+	.open		= xencdrom_block_open,
+	.release	= xencdrom_block_release,
+	.ioctl		= xencdrom_block_ioctl,
+	.media_changed	= xencdrom_block_media_changed,
+};
+
+void register_vcd(struct blkfront_info *info)
+{
+	struct gendisk *gd = info->gd;
+	struct vcd_disk *vcd;
+
+	/* Make sure this is for a CD device */
+	if (!(gd->flags & GENHD_FL_CD))
+		goto out;
+
+	/* Make sure we have backend support */
+	if (!xencdrom_supported(info))
+		goto out;
+
+	/* Create new vcd_disk and fill in cdrom_info */
+	vcd = kzalloc(sizeof(*vcd), GFP_KERNEL);
+	if (!vcd) {
+		pr_info("%s(): Unable to allocate vcd struct!\n", __func__);
+		goto out;
+	}
+	spin_lock_init(&vcd->vcd_cdrom_info_lock);
+
+	vcd->vcd_cdrom_info.ops = &xencdrom_dops;
+	vcd->vcd_cdrom_info.speed = 4;
+	vcd->vcd_cdrom_info.capacity = 1;
+	vcd->vcd_cdrom_info.options = 0;
+	strlcpy(vcd->vcd_cdrom_info.name, gd->disk_name,
+		ARRAY_SIZE(vcd->vcd_cdrom_info.name));
+	vcd->vcd_cdrom_info.mask = (CDC_CD_RW | CDC_DVD_R | CDC_DVD_RAM |
+			CDC_SELECT_DISC | CDC_SELECT_SPEED |
+			CDC_MRW | CDC_MRW_W | CDC_RAM);
+
+	if (register_cdrom(&(vcd->vcd_cdrom_info)) != 0) {
+		pr_warn("%s() Cannot register blkdev as a cdrom %d!\n",
+			__func__, gd->major);
+		goto err_out;
+	}
+	gd->fops = &xencdrom_bdops;
+	vcd->vcd_cdrom_info.disk = gd;
+
+	spin_lock(&vcd_disks_lock);
+	list_add(&(vcd->vcd_entry), &vcd_disks);
+	spin_unlock(&vcd_disks_lock);
+out:
+	return;
+err_out:
+	kfree(vcd);
+}
+
+void unregister_vcd(struct blkfront_info *info) {
+	struct gendisk *gd = info->gd;
+	struct vcd_disk *vcd;
+
+	spin_lock(&vcd_disks_lock);
+	list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
+		if (vcd->vcd_cdrom_info.disk == gd) {
+			spin_lock(&vcd->vcd_cdrom_info_lock);
+			unregister_cdrom(&vcd->vcd_cdrom_info);
+			list_del(&vcd->vcd_entry);
+			spin_unlock(&vcd->vcd_cdrom_info_lock);
+			kfree(vcd);
+			break;
+		}
+	}
+	spin_unlock(&vcd_disks_lock);
+}
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ head/include/xen/interface/io/cdromif.h	2010-11-23 15:06:54.000000000 +0100
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * cdromif.h
+ *
+ * Shared definitions between backend driver and Xen guest Virtual CDROM
+ * block device.
+ *
+ * Copyright (c) 2008, Pat Campell  plc@novell.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_PUBLIC_IO_CDROMIF_H__
+#define __XEN_PUBLIC_IO_CDROMIF_H__
+
+/*
+ * Queries backend for CDROM support
+ */
+#define XEN_TYPE_CDROM_SUPPORT         _IO('c', 1)
+
+struct xen_cdrom_support
+{
+	uint32_t type;
+	int8_t ret;                  /* returned, 0 succeded, -1 error */
+	int8_t err;                  /* returned, backend errno */
+	int8_t supported;            /* returned, 1 supported */
+};
+
+/*
+ * Opens backend device, returns drive geometry or
+ * any encountered errors
+ */
+#define XEN_TYPE_CDROM_OPEN            _IO('c', 2)
+
+struct xen_cdrom_open
+{
+	uint32_t type;
+	int8_t ret;
+	int8_t err;
+	int8_t pad;
+	int8_t media_present;        /* returned */
+	uint32_t sectors;            /* returned */
+	uint32_t sector_size;        /* returned */
+	int32_t payload_offset;      /* offset to backend node name payload */
+};
+
+/*
+ * Queries backend for media changed status
+ */
+#define XEN_TYPE_CDROM_MEDIA_CHANGED   _IO('c', 3)
+
+struct xen_cdrom_media_changed
+{
+	uint32_t type;
+	int8_t ret;
+	int8_t err;
+	int8_t media_changed;        /* returned */
+};
+
+/*
+ * Sends vcd generic CDROM packet to backend, followed
+ * immediately by the vcd_generic_command payload
+ */
+#define XEN_TYPE_CDROM_PACKET          _IO('c', 4)
+
+struct xen_cdrom_packet
+{
+	uint32_t type;
+	int8_t ret;
+	int8_t err;
+	int8_t pad[2];
+	int32_t payload_offset;      /* offset to vcd_generic_command payload */
+};
+
+/* CDROM_PACKET_COMMAND, payload for XEN_TYPE_CDROM_PACKET */
+struct vcd_generic_command
+{
+	uint8_t  cmd[CDROM_PACKET_SIZE];
+	uint8_t  pad[4];
+	uint32_t buffer_offset;
+	uint32_t buflen;
+	int32_t  stat;
+	uint32_t sense_offset;
+	uint8_t  data_direction;
+	uint8_t  pad1[3];
+	int32_t  quiet;
+	int32_t  timeout;
+};
+
+union xen_block_packet
+{
+	uint32_t type;
+	struct xen_cdrom_support xcs;
+	struct xen_cdrom_open xco;
+	struct xen_cdrom_media_changed xcmc;
+	struct xen_cdrom_packet xcp;
+};
+
+#define PACKET_PAYLOAD_OFFSET (sizeof(struct xen_cdrom_packet))
+#define PACKET_SENSE_OFFSET (PACKET_PAYLOAD_OFFSET + sizeof(struct vcd_generic_command))
+#define PACKET_BUFFER_OFFSET (PACKET_SENSE_OFFSET + sizeof(struct request_sense))
+#define MAX_PACKET_DATA (PAGE_SIZE - sizeof(struct xen_cdrom_packet) - \
+            sizeof(struct vcd_generic_command) - sizeof(struct request_sense))
+
+#endif