1 /*******************************************************************************
4 * Implements CDROM cmd packet passing between frontend guest and backend driver.
6 * Copyright (c) 2008, Pat Campell plc@novell.com
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this source file (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use, copy, modify,
11 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
12 * and to permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27 #include <linux/module.h>
28 #include <linux/blkdev.h>
29 #include <linux/list.h>
30 #include <linux/cdrom.h>
31 #include <xen/interface/io/cdromif.h>
34 /* List of cdrom_device_info, can have as many as blkfront supports */
36 struct list_head vcd_entry;
37 struct cdrom_device_info vcd_cdrom_info;
38 spinlock_t vcd_cdrom_info_lock;
40 static LIST_HEAD(vcd_disks);
41 static DEFINE_SPINLOCK(vcd_disks_lock);
43 static struct vcd_disk *xencdrom_get_list_entry(struct gendisk *disk)
45 struct vcd_disk *ret_vcd = NULL;
48 spin_lock(&vcd_disks_lock);
49 list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
50 if (vcd->vcd_cdrom_info.disk == disk) {
51 spin_lock(&vcd->vcd_cdrom_info_lock);
56 spin_unlock(&vcd_disks_lock);
60 static void submit_message(struct blkfront_info *info, void *sp)
62 struct request *req = NULL;
64 req = blk_get_request(info->rq, READ, __GFP_WAIT);
65 if (blk_rq_map_kern(info->rq, req, sp, PAGE_SIZE, __GFP_WAIT))
68 req->rq_disk = info->gd;
69 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
70 req->cmd_type = REQ_TYPE_BLOCK_PC;
71 req->cmd_flags |= REQ_NOMERGE;
73 req->flags |= REQ_BLOCK_PC;
79 blk_execute_rq(req->q, info->gd, req, 1);
85 static int submit_cdrom_cmd(struct blkfront_info *info,
86 struct packet_command *cgc)
90 union xen_block_packet *sp;
91 struct xen_cdrom_packet *xcp;
92 struct vcd_generic_command *vgc;
94 if (cgc->buffer && cgc->buflen > MAX_PACKET_DATA) {
95 pr_warn("%s() Packet buffer length is to large \n", __func__);
99 page = alloc_page(GFP_NOIO|__GFP_ZERO);
101 pr_crit("%s() Unable to allocate page\n", __func__);
105 sp = page_address(page);
107 xcp->type = XEN_TYPE_CDROM_PACKET;
108 xcp->payload_offset = PACKET_PAYLOAD_OFFSET;
110 vgc = (struct vcd_generic_command *)((char *)sp + xcp->payload_offset);
111 memcpy(vgc->cmd, cgc->cmd, CDROM_PACKET_SIZE);
112 vgc->stat = cgc->stat;
113 vgc->data_direction = cgc->data_direction;
114 vgc->quiet = cgc->quiet;
115 vgc->timeout = cgc->timeout;
117 vgc->sense_offset = PACKET_SENSE_OFFSET;
118 memcpy((char *)sp + vgc->sense_offset, cgc->sense, sizeof(struct request_sense));
121 vgc->buffer_offset = PACKET_BUFFER_OFFSET;
122 memcpy((char *)sp + vgc->buffer_offset, cgc->buffer, cgc->buflen);
123 vgc->buflen = cgc->buflen;
126 submit_message(info,sp);
132 memcpy(cgc->sense, (char *)sp + PACKET_SENSE_OFFSET, sizeof(struct request_sense));
133 if (cgc->buffer && cgc->buflen)
134 memcpy(cgc->buffer, (char *)sp + PACKET_BUFFER_OFFSET, cgc->buflen);
141 static int xencdrom_open(struct cdrom_device_info *cdi, int purpose)
145 struct blkfront_info *info;
146 union xen_block_packet *sp;
147 struct xen_cdrom_open *xco;
149 info = cdi->disk->private_data;
154 if (strlen(info->xbdev->otherend) > MAX_PACKET_DATA) {
158 page = alloc_page(GFP_NOIO|__GFP_ZERO);
160 pr_crit("%s() Unable to allocate page\n", __func__);
164 sp = page_address(page);
166 xco->type = XEN_TYPE_CDROM_OPEN;
167 xco->payload_offset = sizeof(struct xen_cdrom_open);
168 strcpy((char *)sp + xco->payload_offset, info->xbdev->otherend);
170 submit_message(info,sp);
177 if (xco->media_present)
178 set_capacity(cdi->disk, xco->sectors);
185 static void xencdrom_release(struct cdrom_device_info *cdi)
189 static int xencdrom_media_changed(struct cdrom_device_info *cdi, int disc_nr)
193 struct blkfront_info *info;
194 union xen_block_packet *sp;
195 struct xen_cdrom_media_changed *xcmc;
197 info = cdi->disk->private_data;
199 page = alloc_page(GFP_NOIO|__GFP_ZERO);
201 pr_crit("%s() Unable to allocate page\n", __func__);
205 sp = page_address(page);
207 xcmc->type = XEN_TYPE_CDROM_MEDIA_CHANGED;
208 submit_message(info,sp);
209 ret = xcmc->media_changed;
216 static int xencdrom_tray_move(struct cdrom_device_info *cdi, int position)
218 struct packet_command cgc;
219 struct blkfront_info *info;
221 info = cdi->disk->private_data;
222 init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
223 cgc.cmd[0] = GPCMD_START_STOP_UNIT;
229 return submit_cdrom_cmd(info, &cgc);
232 static int xencdrom_lock_door(struct cdrom_device_info *cdi, int lock)
234 struct blkfront_info *info;
235 struct packet_command cgc;
237 info = cdi->disk->private_data;
238 init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
239 cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
242 return submit_cdrom_cmd(info, &cgc);
245 static int xencdrom_packet(struct cdrom_device_info *cdi,
246 struct packet_command *cgc)
248 return cgc->stat = submit_cdrom_cmd(cdi->disk->private_data, cgc);
251 static int xencdrom_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
257 /* Query backend to see if CDROM packets are supported */
258 static int xencdrom_supported(struct blkfront_info *info)
261 union xen_block_packet *sp;
262 struct xen_cdrom_support *xcs;
264 page = alloc_page(GFP_NOIO|__GFP_ZERO);
266 pr_crit("%s() Unable to allocate page\n", __func__);
270 sp = page_address(page);
272 xcs->type = XEN_TYPE_CDROM_SUPPORT;
273 submit_message(info,sp);
274 return xcs->supported;
277 static struct cdrom_device_ops xencdrom_dops = {
278 .open = xencdrom_open,
279 .release = xencdrom_release,
280 .media_changed = xencdrom_media_changed,
281 .tray_move = xencdrom_tray_move,
282 .lock_door = xencdrom_lock_door,
283 .generic_packet = xencdrom_packet,
284 .audio_ioctl = xencdrom_audio_ioctl,
285 .capability = (CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | \
286 CDC_MEDIA_CHANGED | CDC_GENERIC_PACKET | CDC_DVD | \
291 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
292 static int xencdrom_block_open(struct inode *inode, struct file *file)
294 struct block_device *bd = inode->i_bdev;
296 static int xencdrom_block_open(struct block_device *bd, fmode_t mode)
299 struct blkfront_info *info = bd->bd_disk->private_data;
300 struct vcd_disk *vcd;
306 if ((vcd = xencdrom_get_list_entry(info->gd))) {
307 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
308 ret = cdrom_open(&vcd->vcd_cdrom_info, inode, file);
310 ret = cdrom_open(&vcd->vcd_cdrom_info, bd, mode);
312 info->users = vcd->vcd_cdrom_info.use_count;
313 spin_unlock(&vcd->vcd_cdrom_info_lock);
319 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
320 static int xencdrom_block_release(struct inode *inode, struct file *file)
322 struct gendisk *gd = inode->i_bdev->bd_disk;
324 static int xencdrom_block_release(struct gendisk *gd, fmode_t mode)
327 struct blkfront_info *info = gd->private_data;
328 struct vcd_disk *vcd;
331 if ((vcd = xencdrom_get_list_entry(info->gd))) {
332 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
333 ret = cdrom_release(&vcd->vcd_cdrom_info, file);
335 cdrom_release(&vcd->vcd_cdrom_info, mode);
337 spin_unlock(&vcd->vcd_cdrom_info_lock);
338 if (vcd->vcd_cdrom_info.use_count == 0) {
340 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
341 blkif_release(inode, file);
343 blkif_release(gd, mode);
351 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
352 static int xencdrom_block_ioctl(struct inode *inode, struct file *file,
353 unsigned cmd, unsigned long arg)
355 struct block_device *bd = inode->i_bdev;
357 static int xencdrom_block_ioctl(struct block_device *bd, fmode_t mode,
358 unsigned cmd, unsigned long arg)
361 struct blkfront_info *info = bd->bd_disk->private_data;
362 struct vcd_disk *vcd;
365 if (!(vcd = xencdrom_get_list_entry(info->gd)))
369 case 2285: /* SG_IO */
373 ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 1);
376 ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 0);
378 case CDROM_GET_CAPABILITY:
379 ret = vcd->vcd_cdrom_info.ops->capability & ~vcd->vcd_cdrom_info.mask;
381 case CDROM_SET_OPTIONS:
382 ret = vcd->vcd_cdrom_info.options;
384 case CDROM_SEND_PACKET: {
385 struct packet_command cgc;
387 ret = copy_from_user(&cgc, (void __user *)arg, sizeof(cgc))
388 ? -EFAULT : submit_cdrom_cmd(info, &cgc);
392 spin_unlock(&vcd->vcd_cdrom_info_lock);
394 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
395 return blkif_ioctl(inode, file, cmd, arg);
397 return blkif_ioctl(bd, mode, cmd, arg);
400 spin_unlock(&vcd->vcd_cdrom_info_lock);
405 /* Called as result of cdrom_open, vcd_cdrom_info_lock already held */
406 static int xencdrom_block_media_changed(struct gendisk *disk)
408 struct vcd_disk *vcd;
409 struct vcd_disk *ret_vcd = NULL;
411 spin_lock(&vcd_disks_lock);
412 list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
413 if (vcd->vcd_cdrom_info.disk == disk) {
418 spin_unlock(&vcd_disks_lock);
420 return ret_vcd ? cdrom_media_changed(&ret_vcd->vcd_cdrom_info) : 0;
423 static const struct block_device_operations xencdrom_bdops =
425 .owner = THIS_MODULE,
426 .open = xencdrom_block_open,
427 .release = xencdrom_block_release,
428 .ioctl = xencdrom_block_ioctl,
429 .media_changed = xencdrom_block_media_changed,
432 void register_vcd(struct blkfront_info *info)
434 struct gendisk *gd = info->gd;
435 struct vcd_disk *vcd;
437 /* Make sure this is for a CD device */
438 if (!(gd->flags & GENHD_FL_CD))
441 /* Make sure we have backend support */
442 if (!xencdrom_supported(info))
445 /* Create new vcd_disk and fill in cdrom_info */
446 vcd = kzalloc(sizeof(*vcd), GFP_KERNEL);
448 pr_info("%s(): Unable to allocate vcd struct!\n", __func__);
451 spin_lock_init(&vcd->vcd_cdrom_info_lock);
453 vcd->vcd_cdrom_info.ops = &xencdrom_dops;
454 vcd->vcd_cdrom_info.speed = 4;
455 vcd->vcd_cdrom_info.capacity = 1;
456 vcd->vcd_cdrom_info.options = 0;
457 strlcpy(vcd->vcd_cdrom_info.name, gd->disk_name,
458 ARRAY_SIZE(vcd->vcd_cdrom_info.name));
459 vcd->vcd_cdrom_info.mask = (CDC_CD_RW | CDC_DVD_R | CDC_DVD_RAM |
460 CDC_SELECT_DISC | CDC_SELECT_SPEED |
461 CDC_MRW | CDC_MRW_W | CDC_RAM);
463 if (register_cdrom(&(vcd->vcd_cdrom_info)) != 0) {
464 pr_warn("%s() Cannot register blkdev as a cdrom %d!\n",
465 __func__, gd->major);
468 gd->fops = &xencdrom_bdops;
469 vcd->vcd_cdrom_info.disk = gd;
471 spin_lock(&vcd_disks_lock);
472 list_add(&(vcd->vcd_entry), &vcd_disks);
473 spin_unlock(&vcd_disks_lock);
480 void unregister_vcd(struct blkfront_info *info) {
481 struct gendisk *gd = info->gd;
482 struct vcd_disk *vcd;
484 spin_lock(&vcd_disks_lock);
485 list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
486 if (vcd->vcd_cdrom_info.disk == gd) {
487 spin_lock(&vcd->vcd_cdrom_info_lock);
488 unregister_cdrom(&vcd->vcd_cdrom_info);
489 list_del(&vcd->vcd_entry);
490 spin_unlock(&vcd->vcd_cdrom_info_lock);
495 spin_unlock(&vcd_disks_lock);