- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / blkfront / vcd.c
1 /*******************************************************************************
2  * vcd.c
3  *
4  * Implements CDROM cmd packet passing between frontend guest and backend driver.
5  *
6  * Copyright (c) 2008, Pat Campell  plc@novell.com
7  *
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:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
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
24  * IN THE SOFTWARE.
25  */
26
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>
32 #include "block.h"
33
34 /* List of cdrom_device_info, can have as many as blkfront supports */
35 struct vcd_disk {
36         struct list_head vcd_entry;
37         struct cdrom_device_info vcd_cdrom_info;
38         spinlock_t vcd_cdrom_info_lock;
39 };
40 static LIST_HEAD(vcd_disks);
41 static DEFINE_SPINLOCK(vcd_disks_lock);
42
43 static struct vcd_disk *xencdrom_get_list_entry(struct gendisk *disk)
44 {
45         struct vcd_disk *ret_vcd = NULL;
46         struct vcd_disk *vcd;
47
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);
52                         ret_vcd = vcd;
53                         break;
54                 }
55         }
56         spin_unlock(&vcd_disks_lock);
57         return ret_vcd;
58 }
59
60 static void submit_message(struct blkfront_info *info, void *sp)
61 {
62         struct request *req = NULL;
63
64         req = blk_get_request(info->rq, READ, __GFP_WAIT);
65         if (blk_rq_map_kern(info->rq, req, sp, PAGE_SIZE, __GFP_WAIT))
66                 goto out;
67
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;
72 #else
73         req->flags |= REQ_BLOCK_PC;
74 #endif
75         req->__sector = 0;
76         req->cmd_len = 0;
77         req->timeout = 60*HZ;
78
79         blk_execute_rq(req->q, info->gd, req, 1);
80
81 out:
82         blk_put_request(req);
83 }
84
85 static int submit_cdrom_cmd(struct blkfront_info *info,
86                             struct packet_command *cgc)
87 {
88         int ret = 0;
89         struct page *page;
90         union xen_block_packet *sp;
91         struct xen_cdrom_packet *xcp;
92         struct vcd_generic_command *vgc;
93
94         if (cgc->buffer && cgc->buflen > MAX_PACKET_DATA) {
95                 pr_warn("%s() Packet buffer length is to large \n", __func__);
96                 return -EIO;
97         }
98
99         page = alloc_page(GFP_NOIO|__GFP_ZERO);
100         if (!page) {
101                 pr_crit("%s() Unable to allocate page\n", __func__);
102                 return -ENOMEM;
103         }
104
105         sp = page_address(page);
106         xcp = &(sp->xcp);
107         xcp->type = XEN_TYPE_CDROM_PACKET;
108         xcp->payload_offset = PACKET_PAYLOAD_OFFSET;
109
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;
116         if (cgc->sense) {
117                 vgc->sense_offset = PACKET_SENSE_OFFSET;
118                 memcpy((char *)sp + vgc->sense_offset, cgc->sense, sizeof(struct request_sense));
119         }
120         if (cgc->buffer) {
121                 vgc->buffer_offset = PACKET_BUFFER_OFFSET;
122                 memcpy((char *)sp + vgc->buffer_offset, cgc->buffer, cgc->buflen);
123                 vgc->buflen = cgc->buflen;
124         }
125
126         submit_message(info,sp);
127
128         if (xcp->ret)
129                 ret = xcp->err;
130
131         if (cgc->sense)
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);
135
136         __free_page(page);
137         return ret;
138 }
139
140
141 static int xencdrom_open(struct cdrom_device_info *cdi, int purpose)
142 {
143         int ret = 0;
144         struct page *page;
145         struct blkfront_info *info;
146         union xen_block_packet *sp;
147         struct xen_cdrom_open *xco;
148
149         info = cdi->disk->private_data;
150
151         if (!info->xbdev)
152                 return -ENODEV;
153
154         if (strlen(info->xbdev->otherend) > MAX_PACKET_DATA) {
155                 return -EIO;
156         }
157
158         page = alloc_page(GFP_NOIO|__GFP_ZERO);
159         if (!page) {
160                 pr_crit("%s() Unable to allocate page\n", __func__);
161                 return -ENOMEM;
162         }
163
164         sp = page_address(page);
165         xco = &(sp->xco);
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);
169
170         submit_message(info,sp);
171
172         if (xco->ret) {
173                 ret = xco->err;
174                 goto out;
175         }
176
177         if (xco->media_present)
178                 set_capacity(cdi->disk, xco->sectors);
179
180 out:
181         __free_page(page);
182         return ret;
183 }
184
185 static void xencdrom_release(struct cdrom_device_info *cdi)
186 {
187 }
188
189 static int xencdrom_media_changed(struct cdrom_device_info *cdi, int disc_nr)
190 {
191         int ret;
192         struct page *page;
193         struct blkfront_info *info;
194         union xen_block_packet *sp;
195         struct xen_cdrom_media_changed *xcmc;
196
197         info = cdi->disk->private_data;
198
199         page = alloc_page(GFP_NOIO|__GFP_ZERO);
200         if (!page) {
201                 pr_crit("%s() Unable to allocate page\n", __func__);
202                 return -ENOMEM;
203         }
204
205         sp = page_address(page);
206         xcmc = &(sp->xcmc);
207         xcmc->type = XEN_TYPE_CDROM_MEDIA_CHANGED;
208         submit_message(info,sp);
209         ret = xcmc->media_changed;
210
211         __free_page(page);
212
213         return ret;
214 }
215
216 static int xencdrom_tray_move(struct cdrom_device_info *cdi, int position)
217 {
218         struct packet_command cgc;
219         struct blkfront_info *info;
220
221         info = cdi->disk->private_data;
222         init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
223         cgc.cmd[0] = GPCMD_START_STOP_UNIT;
224         if (position)
225                 cgc.cmd[4] = 2;
226         else
227                 cgc.cmd[4] = 3;
228
229         return submit_cdrom_cmd(info, &cgc);
230 }
231
232 static int xencdrom_lock_door(struct cdrom_device_info *cdi, int lock)
233 {
234         struct blkfront_info *info;
235         struct packet_command cgc;
236
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;
240         cgc.cmd[4] = lock;
241
242         return submit_cdrom_cmd(info, &cgc);
243 }
244
245 static int xencdrom_packet(struct cdrom_device_info *cdi,
246                            struct packet_command *cgc)
247 {
248         return cgc->stat = submit_cdrom_cmd(cdi->disk->private_data, cgc);
249 }
250
251 static int xencdrom_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
252                 void *arg)
253 {
254         return -EINVAL;
255 }
256
257 /* Query backend to see if CDROM packets are supported */
258 static int xencdrom_supported(struct blkfront_info *info)
259 {
260         struct page *page;
261         union xen_block_packet *sp;
262         struct xen_cdrom_support *xcs;
263
264         page = alloc_page(GFP_NOIO|__GFP_ZERO);
265         if (!page) {
266                 pr_crit("%s() Unable to allocate page\n", __func__);
267                 return -ENOMEM;
268         }
269
270         sp = page_address(page);
271         xcs = &(sp->xcs);
272         xcs->type = XEN_TYPE_CDROM_SUPPORT;
273         submit_message(info,sp);
274         return xcs->supported;
275 }
276
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 | \
287                        CDC_CD_R),
288     .n_minors       = 1,
289 };
290
291 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
292 static int xencdrom_block_open(struct inode *inode, struct file *file)
293 {
294         struct block_device *bd = inode->i_bdev;
295 #else
296 static int xencdrom_block_open(struct block_device *bd, fmode_t mode)
297 {
298 #endif
299         struct blkfront_info *info = bd->bd_disk->private_data;
300         struct vcd_disk *vcd;
301         int ret = 0;
302
303         if (!info->xbdev)
304                 return -ENODEV;
305
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);
309 #else
310                 ret = cdrom_open(&vcd->vcd_cdrom_info, bd, mode);
311 #endif
312                 info->users = vcd->vcd_cdrom_info.use_count;
313                 spin_unlock(&vcd->vcd_cdrom_info_lock);
314         }
315
316         return ret;
317 }
318
319 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
320 static int xencdrom_block_release(struct inode *inode, struct file *file)
321 {
322         struct gendisk *gd = inode->i_bdev->bd_disk;
323 #else
324 static int xencdrom_block_release(struct gendisk *gd, fmode_t mode)
325 {
326 #endif
327         struct blkfront_info *info = gd->private_data;
328         struct vcd_disk *vcd;
329         int ret = 0;
330
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);
334 #else
335                 cdrom_release(&vcd->vcd_cdrom_info, mode);
336 #endif
337                 spin_unlock(&vcd->vcd_cdrom_info_lock);
338                 if (vcd->vcd_cdrom_info.use_count == 0) {
339                         info->users = 1;
340 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
341                         blkif_release(inode, file);
342 #else
343                         blkif_release(gd, mode);
344 #endif
345                 }
346         }
347
348         return ret;
349 }
350
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)
354 {
355         struct block_device *bd = inode->i_bdev;
356 #else
357 static int xencdrom_block_ioctl(struct block_device *bd, fmode_t mode,
358                                 unsigned cmd, unsigned long arg)
359 {
360 #endif
361         struct blkfront_info *info = bd->bd_disk->private_data;
362         struct vcd_disk *vcd;
363         int ret = 0;
364
365         if (!(vcd = xencdrom_get_list_entry(info->gd)))
366                 goto out;
367
368         switch (cmd) {
369         case 2285: /* SG_IO */
370                 ret = -ENOSYS;
371                 break;
372         case CDROMEJECT:
373                 ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 1);
374                 break;
375         case CDROMCLOSETRAY:
376                 ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 0);
377                 break;
378         case CDROM_GET_CAPABILITY:
379                 ret = vcd->vcd_cdrom_info.ops->capability & ~vcd->vcd_cdrom_info.mask;
380                 break;
381         case CDROM_SET_OPTIONS:
382                 ret = vcd->vcd_cdrom_info.options;
383                 break;
384         case CDROM_SEND_PACKET: {
385                 struct packet_command cgc;
386
387                 ret = copy_from_user(&cgc, (void __user *)arg, sizeof(cgc))
388                       ? -EFAULT : submit_cdrom_cmd(info, &cgc);
389                 break;
390         }
391         default:
392                 spin_unlock(&vcd->vcd_cdrom_info_lock);
393 out:
394 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
395                 return blkif_ioctl(inode, file, cmd, arg);
396 #else
397                 return blkif_ioctl(bd, mode, cmd, arg);
398 #endif
399         }
400         spin_unlock(&vcd->vcd_cdrom_info_lock);
401
402         return ret;
403 }
404
405 /* Called as result of cdrom_open, vcd_cdrom_info_lock already held */
406 static int xencdrom_block_media_changed(struct gendisk *disk)
407 {
408         struct vcd_disk *vcd;
409         struct vcd_disk *ret_vcd = NULL;
410
411         spin_lock(&vcd_disks_lock);
412         list_for_each_entry(vcd, &vcd_disks, vcd_entry) {
413                 if (vcd->vcd_cdrom_info.disk == disk) {
414                         ret_vcd = vcd;
415                         break;
416                 }
417         }
418         spin_unlock(&vcd_disks_lock);
419
420         return ret_vcd ? cdrom_media_changed(&ret_vcd->vcd_cdrom_info) : 0;
421 }
422
423 static const struct block_device_operations xencdrom_bdops =
424 {
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,
430 };
431
432 void register_vcd(struct blkfront_info *info)
433 {
434         struct gendisk *gd = info->gd;
435         struct vcd_disk *vcd;
436
437         /* Make sure this is for a CD device */
438         if (!(gd->flags & GENHD_FL_CD))
439                 goto out;
440
441         /* Make sure we have backend support */
442         if (!xencdrom_supported(info))
443                 goto out;
444
445         /* Create new vcd_disk and fill in cdrom_info */
446         vcd = kzalloc(sizeof(*vcd), GFP_KERNEL);
447         if (!vcd) {
448                 pr_info("%s(): Unable to allocate vcd struct!\n", __func__);
449                 goto out;
450         }
451         spin_lock_init(&vcd->vcd_cdrom_info_lock);
452
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);
462
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);
466                 goto err_out;
467         }
468         gd->fops = &xencdrom_bdops;
469         vcd->vcd_cdrom_info.disk = gd;
470
471         spin_lock(&vcd_disks_lock);
472         list_add(&(vcd->vcd_entry), &vcd_disks);
473         spin_unlock(&vcd_disks_lock);
474 out:
475         return;
476 err_out:
477         kfree(vcd);
478 }
479
480 void unregister_vcd(struct blkfront_info *info) {
481         struct gendisk *gd = info->gd;
482         struct vcd_disk *vcd;
483
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);
491                         kfree(vcd);
492                         break;
493                 }
494         }
495         spin_unlock(&vcd_disks_lock);
496 }