Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / usbback / interface.c
1 /*
2  * interface.c
3  *
4  * Xen USB backend interface management.
5  *
6  * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
7  * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  *
22  * or, by your choice,
23  *
24  * When distributed separately from the Linux kernel or incorporated into
25  * other software packages, subject to the following license:
26  *
27  * Permission is hereby granted, free of charge, to any person obtaining a copy
28  * of this software and associated documentation files (the "Software"), to
29  * deal in the Software without restriction, including without limitation the
30  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
31  * sell copies of the Software, and to permit persons to whom the Software is
32  * furnished to do so, subject to the following conditions:
33  *
34  * The above copyright notice and this permission notice shall be included in
35  * all copies or substantial portions of the Software.
36  *
37  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
42  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
43  * DEALINGS IN THE SOFTWARE.
44  */
45
46 #include <linux/vmalloc.h>
47 #include "usbback.h"
48 #include <xen/evtchn.h>
49
50 static LIST_HEAD(usbif_list);
51 static DEFINE_SPINLOCK(usbif_list_lock);
52
53 usbif_t *find_usbif(domid_t domid, unsigned int handle)
54 {
55         usbif_t *usbif;
56         int found = 0;
57         unsigned long flags;
58
59         spin_lock_irqsave(&usbif_list_lock, flags);
60         list_for_each_entry(usbif, &usbif_list, usbif_list) {
61                 if (usbif->domid == domid
62                         && usbif->handle == handle) {
63                         found = 1;
64                         break;
65                 }
66         }
67         spin_unlock_irqrestore(&usbif_list_lock, flags);
68
69         if (found)
70                 return usbif;
71
72         return NULL;
73 }
74
75 usbif_t *usbif_alloc(domid_t domid, unsigned int handle)
76 {
77         usbif_t *usbif;
78         unsigned long flags;
79         int i;
80
81         usbif = kzalloc(sizeof(usbif_t), GFP_KERNEL);
82         if (!usbif)
83                 return NULL;
84
85         usbif->domid = domid;
86         usbif->handle = handle;
87         spin_lock_init(&usbif->urb_ring_lock);
88         spin_lock_init(&usbif->conn_ring_lock);
89         atomic_set(&usbif->refcnt, 0);
90         init_waitqueue_head(&usbif->wq);
91         init_waitqueue_head(&usbif->waiting_to_free);
92         spin_lock_init(&usbif->stub_lock);
93         INIT_LIST_HEAD(&usbif->stub_list);
94         spin_lock_init(&usbif->addr_lock);
95         for (i = 0; i < USB_DEV_ADDR_SIZE; i++)
96                 usbif->addr_table[i] = NULL;
97
98         spin_lock_irqsave(&usbif_list_lock, flags);
99         list_add(&usbif->usbif_list, &usbif_list);
100         spin_unlock_irqrestore(&usbif_list_lock, flags);
101
102         return usbif;
103 }
104
105 int usbif_map(usbif_t *usbif, grant_ref_t urb_ring_ref,
106               grant_ref_t conn_ring_ref, evtchn_port_t evtchn)
107 {
108         int err = -ENOMEM;
109         struct vm_struct *area;
110         usbif_urb_sring_t *urb_sring;
111         usbif_conn_sring_t *conn_sring;
112
113         if (usbif->irq)
114                 return 0;
115
116         area = xenbus_map_ring_valloc(usbif->xbdev, urb_ring_ref);
117         if (IS_ERR(area))
118                 return PTR_ERR(area);
119         usbif->urb_ring_area = area;
120         area = xenbus_map_ring_valloc(usbif->xbdev, conn_ring_ref);
121         if (IS_ERR(area)) {
122                 err = PTR_ERR(area);
123                 goto fail_alloc;
124         }
125         usbif->conn_ring_area = area;
126
127         err = bind_interdomain_evtchn_to_irqhandler(
128                         usbif->domid, evtchn, usbbk_be_int, 0,
129                         "usbif-backend", usbif);
130         if (err < 0)
131                 goto fail_evtchn;
132         usbif->irq = err;
133
134         urb_sring = (usbif_urb_sring_t *) usbif->urb_ring_area->addr;
135         BACK_RING_INIT(&usbif->urb_ring, urb_sring, PAGE_SIZE);
136
137         conn_sring = (usbif_conn_sring_t *) usbif->conn_ring_area->addr;
138         BACK_RING_INIT(&usbif->conn_ring, conn_sring, PAGE_SIZE);
139
140         return 0;
141
142 fail_evtchn:
143         xenbus_unmap_ring_vfree(usbif->xbdev, usbif->conn_ring_area);
144 fail_alloc:
145         xenbus_unmap_ring_vfree(usbif->xbdev, usbif->urb_ring_area);
146
147         return err;
148 }
149
150 void usbif_disconnect(usbif_t *usbif)
151 {
152         struct usbstub *stub, *tmp;
153         unsigned long flags;
154
155         if (usbif->xenusbd) {
156                 kthread_stop(usbif->xenusbd);
157                 usbif->xenusbd = NULL;
158         }
159
160         spin_lock_irqsave(&usbif->stub_lock, flags);
161         list_for_each_entry_safe(stub, tmp, &usbif->stub_list, dev_list) {
162                 usbbk_unlink_urbs(stub);
163                 detach_device_without_lock(usbif, stub);
164         }
165         spin_unlock_irqrestore(&usbif->stub_lock, flags);
166
167         wait_event(usbif->waiting_to_free, atomic_read(&usbif->refcnt) == 0);
168
169         if (usbif->irq) {
170                 unbind_from_irqhandler(usbif->irq, usbif);
171                 usbif->irq = 0;
172         }
173
174         if (usbif->urb_ring.sring) {
175                 xenbus_unmap_ring_vfree(usbif->xbdev, usbif->urb_ring_area);
176                 xenbus_unmap_ring_vfree(usbif->xbdev, usbif->conn_ring_area);
177                 usbif->urb_ring.sring = NULL;
178                 usbif->conn_ring.sring = NULL;
179         }
180 }
181
182 void usbif_free(usbif_t *usbif)
183 {
184         unsigned long flags;
185
186         spin_lock_irqsave(&usbif_list_lock, flags);
187         list_del(&usbif->usbif_list);
188         spin_unlock_irqrestore(&usbif_list_lock, flags);
189         kfree(usbif);
190 }