From: plc@novell.com Subject: implement forwarding of CD-ROM specific commands Patch-mainline: obsolete References: fate#300964 Index: head-2008-10-24/drivers/cdrom/Makefile =================================================================== --- head-2008-10-24.orig/drivers/cdrom/Makefile 2008-10-24 14:05:33.000000000 +0200 +++ head-2008-10-24/drivers/cdrom/Makefile 2008-10-01 16:35:07.000000000 +0200 @@ -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 Index: head-2008-10-24/drivers/xen/blkfront/Makefile =================================================================== --- head-2008-10-24.orig/drivers/xen/blkfront/Makefile 2008-10-24 14:05:33.000000000 +0200 +++ head-2008-10-24/drivers/xen/blkfront/Makefile 2008-10-01 16:35:07.000000000 +0200 @@ -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 Index: head-2008-10-24/drivers/xen/blkfront/blkfront.c =================================================================== --- head-2008-10-24.orig/drivers/xen/blkfront/blkfront.c 2008-10-01 16:35:04.000000000 +0200 +++ head-2008-10-24/drivers/xen/blkfront/blkfront.c 2008-10-01 16:35:07.000000000 +0200 @@ -372,6 +372,8 @@ static void connect(struct blkfront_info add_disk(info->gd); info->is_ready = 1; + + register_vcd(info); } /** @@ -402,6 +404,8 @@ static void blkfront_closing(struct xenb xlvbd_sysfs_delif(info); + unregister_vcd(info); + xlvbd_del(info); out: Index: head-2008-10-24/drivers/xen/blkfront/block.h =================================================================== --- head-2008-10-24.orig/drivers/xen/blkfront/block.h 2008-10-24 14:05:33.000000000 +0200 +++ head-2008-10-24/drivers/xen/blkfront/block.h 2008-10-01 16:35:07.000000000 +0200 @@ -154,4 +154,8 @@ static inline void xlvbd_sysfs_delif(str } #endif +/* 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__ */ Index: head-2008-10-24/drivers/xen/blkfront/vbd.c =================================================================== --- head-2008-10-24.orig/drivers/xen/blkfront/vbd.c 2008-10-24 14:05:33.000000000 +0200 +++ head-2008-10-24/drivers/xen/blkfront/vbd.c 2008-10-24 14:08:33.000000000 +0200 @@ -281,7 +281,8 @@ 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)) == 0) nr_minors = 1 << mi->type->partn_shift; gd = alloc_disk(nr_minors); @@ -290,7 +291,7 @@ xlvbd_add(blkif_sector_t capacity, int v offset = mi->index * mi->type->disks_per_major + (minor >> mi->type->partn_shift); - if (nr_minors > 1) { + if (nr_minors > 1 || (vdisk_info & VDISK_CDROM)) { if (offset < 26) { sprintf(gd->disk_name, "%s%c", mi->type->diskname, 'a' + offset ); Index: head-2008-10-24/drivers/xen/blkfront/vcd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-10-24/drivers/xen/blkfront/vcd.c 2008-10-01 16:35:07.000000000 +0200 @@ -0,0 +1,476 @@ +/******************************************************************************* +* 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. +*/ + +#define REVISION "$Revision: 1.0 $" + +#include +#include +#include +#include +#include +#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->nr_sectors = 1; + 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; + size_t size; + union xen_block_packet *sp; + struct xen_cdrom_packet *xcp; + struct vcd_generic_command * vgc; + + if (cgc->buffer && cgc->buflen > MAX_PACKET_DATA) { + printk(KERN_WARNING "%s() Packet buffer length is to large \n", __func__); + return -EIO; + } + + page = alloc_page(GFP_NOIO); + if (!page) { + printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); + return -ENOMEM; + } + + size = PAGE_SIZE; + memset(page_address(page), 0, PAGE_SIZE); + 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 (strlen(info->xbdev->otherend) > MAX_PACKET_DATA) { + return -EIO; + } + + page = alloc_page(GFP_NOIO); + if (!page) { + printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); + return -ENOMEM; + } + + memset(page_address(page), 0, PAGE_SIZE); + 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); + if (!page) { + printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); + return -ENOMEM; + } + + memset(page_address(page), 0, PAGE_SIZE); + 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) +{ + int ret; + 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; + ret = submit_cdrom_cmd(info, &cgc); + return ret; +} + +static int xencdrom_lock_door(struct cdrom_device_info *cdi, int lock) +{ + int ret = 0; + 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; + ret = submit_cdrom_cmd(info, &cgc); + return ret; +} + +static int xencdrom_packet(struct cdrom_device_info *cdi, + struct packet_command *cgc) +{ + int ret = -EIO; + struct blkfront_info *info; + + info = cdi->disk->private_data; + ret = submit_cdrom_cmd(info, cgc); + cgc->stat = ret; + return ret; +} + +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); + if (!page) { + printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); + return -ENOMEM; + } + + memset(page_address(page), 0, PAGE_SIZE); + 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, +}; + +static int xencdrom_block_open(struct inode *inode, struct file *file) +{ + struct blkfront_info *info = inode->i_bdev->bd_disk->private_data; + struct vcd_disk * vcd; + int ret = 0; + + if ((vcd = xencdrom_get_list_entry(info->gd))) { + ret = cdrom_open(&vcd->vcd_cdrom_info, inode, file); + info->users = vcd->vcd_cdrom_info.use_count; + spin_unlock(&vcd->vcd_cdrom_info_lock); + } + return ret; +} + +static int xencdrom_block_release(struct inode *inode, struct file *file) +{ + struct blkfront_info *info = inode->i_bdev->bd_disk->private_data; + struct vcd_disk * vcd; + int ret = 0; + + if ((vcd = xencdrom_get_list_entry(info->gd))) { + ret = cdrom_release(&vcd->vcd_cdrom_info, file); + spin_unlock(&vcd->vcd_cdrom_info_lock); + if (vcd->vcd_cdrom_info.use_count == 0) { + info->users = 1; + blkif_release(inode, file); + } + } + return ret; +} + +static int xencdrom_block_ioctl(struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg) +{ + struct blkfront_info *info = inode->i_bdev->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 = (struct packet_command *)arg; + ret = submit_cdrom_cmd(info, cgc); + } + break; + default: + /* Not supported, augment supported above if necessary */ + printk( "%s():%d Unsupported IOCTL:%x \n", __func__, __LINE__, cmd); + ret = -ENOTTY; + break; + } + spin_unlock(&vcd->vcd_cdrom_info_lock); +out: + 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; + int ret = 0; + + 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); + if (ret_vcd) { + ret = cdrom_media_changed(&ret_vcd->vcd_cdrom_info); + } + return ret; +} + +static 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 = (struct vcd_disk *)kzalloc(sizeof(struct vcd_disk), GFP_KERNEL); + if (!vcd) { + printk(KERN_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; + strcpy(vcd->vcd_cdrom_info.name, gd->disk_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) { + printk(KERN_WARNING "%s() Cannot register blkdev as a cdrom %d!\n", __func__, + gd->major); + goto err_out; + } + xencdrom_bdops.owner = gd->fops->owner; + 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); +} + Index: head-2008-10-24/include/xen/interface/io/cdromif.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-10-24/include/xen/interface/io/cdromif.h 2008-10-01 16:35:07.000000000 +0200 @@ -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