Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / blkback / cdrom.c
1 /******************************************************************************
2  * blkback/cdrom.c
3  *
4  * Routines for managing cdrom watch and media-present attribute of a
5  * cdrom type virtual block device (VBD).
6  *
7  * Copyright (c) 2003-2005, Keir Fraser & Steve Hand
8  * Copyright (c) 2007       Pat Campbell
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation; or, when distributed
13  * separately from the Linux kernel or incorporated into other
14  * software packages, subject to the following license:
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining a copy
17  * of this source file (the "Software"), to deal in the Software without
18  * restriction, including without limitation the rights to use, copy, modify,
19  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
20  * and to permit persons to whom the Software is furnished to do so, subject to
21  * the following conditions:
22  *
23  * The above copyright notice and this permission notice shall be included in
24  * all copies or substantial portions of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
32  * IN THE SOFTWARE.
33  */
34
35 #include "common.h"
36
37 #define MEDIA_PRESENT "media-present"
38
39 static void cdrom_media_changed(struct xenbus_watch *, const char **, unsigned int);
40
41 /**
42  * Writes media-present=1 attribute for the given vbd device if not
43  * already there
44  */
45 static int cdrom_xenstore_write_media_present(struct backend_info *be)
46 {
47         struct xenbus_device *dev = be->dev;
48         struct xenbus_transaction xbt;
49         int err;
50         int media_present;
51
52         err = xenbus_scanf(XBT_NIL, dev->nodename, MEDIA_PRESENT, "%d",
53                            &media_present);
54         if (0 < err) {
55                 DPRINTK("already written err%d", err);
56                 return(0);
57         }
58         media_present = !!be->blkif->vbd.bdev;
59
60 again:
61         err = xenbus_transaction_start(&xbt);
62         if (err) {
63                 xenbus_dev_fatal(dev, err, "starting transaction");
64                 return(-1);
65         }
66
67         err = xenbus_printf(xbt, dev->nodename, MEDIA_PRESENT, "%d", media_present );
68         if (err) {
69                 xenbus_dev_fatal(dev, err, "writing %s/%s",
70                          dev->nodename, MEDIA_PRESENT);
71                 goto abort;
72         }
73         err = xenbus_transaction_end(xbt, 0);
74         if (err == -EAGAIN)
75                 goto again;
76         if (err)
77                 xenbus_dev_fatal(dev, err, "ending transaction");
78         return 0;
79  abort:
80         xenbus_transaction_end(xbt, 1);
81         return -1;
82 }
83
84 /**
85  *
86  */
87 static int cdrom_is_type(struct backend_info *be)
88 {
89         DPRINTK("type:%x", be->blkif->vbd.type );
90         return (be->blkif->vbd.type & VDISK_CDROM)
91                && (be->blkif->vbd.type & GENHD_FL_REMOVABLE);
92 }
93
94 /**
95  *
96  */
97 void cdrom_add_media_watch(struct backend_info *be)
98 {
99         struct xenbus_device *dev = be->dev;
100         int err;
101
102         DPRINTK("nodename:%s", dev->nodename);
103         if (cdrom_is_type(be)) {
104                 DPRINTK("is a cdrom");
105                 if (cdrom_xenstore_write_media_present(be) == 0) {
106                         DPRINTK("xenstore wrote OK");
107                         err = xenbus_watch_path2(dev, dev->nodename, MEDIA_PRESENT,
108                                                  &be->cdrom_watch,
109                                                  cdrom_media_changed);
110                         if (err)
111                                 DPRINTK(MEDIA_PRESENT " watch add failed");
112                 }
113         }
114 }
115
116 /**
117  * Callback received when the MEDIA_PRESENT xenstore node is changed
118  */
119 static void cdrom_media_changed(struct xenbus_watch *watch,
120                                 const char **vec, unsigned int len)
121 {
122         int err, media_present;
123         struct backend_info *be
124                 = container_of(watch, struct backend_info, cdrom_watch);
125         struct xenbus_device *dev = be->dev;
126
127         if (!cdrom_is_type(be)) {
128                 DPRINTK("callback not for a cdrom" );
129                 return;
130         }
131
132         err = xenbus_scanf(XBT_NIL, dev->nodename, MEDIA_PRESENT, "%d",
133                            &media_present);
134         if (err <= 0) {
135                 DPRINTK("read of " MEDIA_PRESENT " node error:%d", err);
136                 return;
137         }
138
139         if (!media_present)
140                 vbd_free(&be->blkif->vbd);
141         else if (!be->blkif->vbd.bdev) {
142                 char *p = strrchr(dev->otherend, '/') + 1;
143                 long handle = simple_strtoul(p, NULL, 0);
144
145                 err = vbd_create(be->blkif, handle, be->major, be->minor,
146                                  be->blkif->vbd.mode, true);
147                 if (err && err != -ENOMEDIUM) {
148                         be->major = be->minor = 0;
149                         xenbus_dev_fatal(dev, err, "creating vbd structure");
150                         return;
151                 }
152                 vbd_resize(be->blkif);
153         }
154 }