• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2003-2008 Takahiro Hirofuchi
3  *
4  * This is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17  * USA.
18  */
19 
20 
21 #include "usbip_common.h"
22 #include "stub.h"
23 
24 /* Version Information */
25 #define DRIVER_VERSION "1.0"
26 #define DRIVER_AUTHOR "Takahiro Hirofuchi"
27 #define DRIVER_DESC "Stub Driver for USB/IP"
28 
29 /* stub_priv is allocated from stub_priv_cache */
30 struct kmem_cache *stub_priv_cache;
31 
32 /*-------------------------------------------------------------------------*/
33 
34 /* Define sysfs entries for the usbip driver */
35 
36 
37 /*
38  * busid_tables defines matching busids that usbip can grab. A user can change
39  * dynamically what device is locally used and what device is exported to a
40  * remote host.
41  */
42 #define MAX_BUSID 16
43 #define BUSID_SIZE 20
44 static char busid_table[MAX_BUSID][BUSID_SIZE];
45 static spinlock_t busid_table_lock;
46 
47 
match_busid(const char * busid)48 int match_busid(const char *busid)
49 {
50 	int i;
51 
52 	spin_lock(&busid_table_lock);
53 
54 	for (i = 0; i < MAX_BUSID; i++)
55 		if (busid_table[i][0])
56 			if (!strncmp(busid_table[i], busid, BUSID_SIZE)) {
57 				/* already registerd */
58 				spin_unlock(&busid_table_lock);
59 				return 0;
60 			}
61 
62 	spin_unlock(&busid_table_lock);
63 
64 	return 1;
65 }
66 
show_match_busid(struct device_driver * drv,char * buf)67 static ssize_t show_match_busid(struct device_driver *drv, char *buf)
68 {
69 	int i;
70 	char *out = buf;
71 
72 	spin_lock(&busid_table_lock);
73 
74 	for (i = 0; i < MAX_BUSID; i++)
75 		if (busid_table[i][0])
76 			out += sprintf(out, "%s ", busid_table[i]);
77 
78 	spin_unlock(&busid_table_lock);
79 
80 	out += sprintf(out, "\n");
81 
82 	return out - buf;
83 }
84 
add_match_busid(char * busid)85 static int add_match_busid(char *busid)
86 {
87 	int i;
88 
89 	if (!match_busid(busid))
90 		return 0;
91 
92 	spin_lock(&busid_table_lock);
93 
94 	for (i = 0; i < MAX_BUSID; i++)
95 		if (!busid_table[i][0]) {
96 			strncpy(busid_table[i], busid, BUSID_SIZE);
97 			spin_unlock(&busid_table_lock);
98 			return 0;
99 		}
100 
101 	spin_unlock(&busid_table_lock);
102 
103 	return -1;
104 }
105 
del_match_busid(char * busid)106 static int del_match_busid(char *busid)
107 {
108 	int i;
109 
110 	spin_lock(&busid_table_lock);
111 
112 	for (i = 0; i < MAX_BUSID; i++)
113 		if (!strncmp(busid_table[i], busid, BUSID_SIZE)) {
114 			/* found */
115 			memset(busid_table[i], 0, BUSID_SIZE);
116 			spin_unlock(&busid_table_lock);
117 			return 0;
118 		}
119 
120 	spin_unlock(&busid_table_lock);
121 
122 	return -1;
123 }
124 
store_match_busid(struct device_driver * dev,const char * buf,size_t count)125 static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
126 		size_t count)
127 {
128 	int len;
129 	char busid[BUSID_SIZE];
130 
131 	if (count < 5)
132 		return -EINVAL;
133 
134 	/* strnlen() does not include \0 */
135 	len = strnlen(buf + 4, BUSID_SIZE);
136 
137 	/* busid needs to include \0 termination */
138 	if (!(len < BUSID_SIZE))
139 		return -EINVAL;
140 
141 	strncpy(busid, buf + 4, BUSID_SIZE);
142 
143 
144 	if (!strncmp(buf, "add ", 4)) {
145 		if (add_match_busid(busid) < 0)
146 			return -ENOMEM;
147 		else {
148 			udbg("add busid %s\n", busid);
149 			return count;
150 		}
151 	} else if (!strncmp(buf, "del ", 4)) {
152 		if (del_match_busid(busid) < 0)
153 			return -ENODEV;
154 		else {
155 			udbg("del busid %s\n", busid);
156 			return count;
157 		}
158 	} else
159 		return -EINVAL;
160 }
161 
162 static DRIVER_ATTR(match_busid, S_IRUSR|S_IWUSR, show_match_busid,
163 							store_match_busid);
164 
165 
166 
167 /*-------------------------------------------------------------------------*/
168 
169 /* Cleanup functions used to free private data */
170 
stub_priv_pop_from_listhead(struct list_head * listhead)171 static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
172 {
173 	struct stub_priv *priv, *tmp;
174 
175 	list_for_each_entry_safe(priv, tmp, listhead, list) {
176 		list_del(&priv->list);
177 		return priv;
178 	}
179 
180 	return NULL;
181 }
182 
stub_priv_pop(struct stub_device * sdev)183 static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
184 {
185 	unsigned long flags;
186 	struct stub_priv *priv;
187 
188 	spin_lock_irqsave(&sdev->priv_lock, flags);
189 
190 	priv = stub_priv_pop_from_listhead(&sdev->priv_init);
191 	if (priv) {
192 		spin_unlock_irqrestore(&sdev->priv_lock, flags);
193 		return priv;
194 	}
195 
196 	priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
197 	if (priv) {
198 		spin_unlock_irqrestore(&sdev->priv_lock, flags);
199 		return priv;
200 	}
201 
202 	priv = stub_priv_pop_from_listhead(&sdev->priv_free);
203 	if (priv) {
204 		spin_unlock_irqrestore(&sdev->priv_lock, flags);
205 		return priv;
206 	}
207 
208 	spin_unlock_irqrestore(&sdev->priv_lock, flags);
209 	return NULL;
210 }
211 
stub_device_cleanup_urbs(struct stub_device * sdev)212 void stub_device_cleanup_urbs(struct stub_device *sdev)
213 {
214 	struct stub_priv *priv;
215 
216 	udbg("free sdev %p\n", sdev);
217 
218 	while ((priv = stub_priv_pop(sdev))) {
219 		struct urb *urb = priv->urb;
220 
221 		udbg("   free urb %p\n", urb);
222 		usb_kill_urb(urb);
223 
224 		kmem_cache_free(stub_priv_cache, priv);
225 
226 		if (urb->transfer_buffer != NULL)
227 			kfree(urb->transfer_buffer);
228 
229 		if (urb->setup_packet != NULL)
230 			kfree(urb->setup_packet);
231 
232 		usb_free_urb(urb);
233 	}
234 }
235 
236 
237 /*-------------------------------------------------------------------------*/
238 
usb_stub_init(void)239 static int __init usb_stub_init(void)
240 {
241 	int ret;
242 
243 	stub_priv_cache = kmem_cache_create("stub_priv",
244 					    sizeof(struct stub_priv), 0,
245 					    SLAB_HWCACHE_ALIGN, NULL);
246 
247 	if (!stub_priv_cache) {
248 		printk(KERN_ERR KBUILD_MODNAME
249 		       ": create stub_priv_cache error\n");
250 		return -ENOMEM;
251 	}
252 
253 	ret = usb_register(&stub_driver);
254 	if (ret) {
255 		printk(KERN_ERR KBUILD_MODNAME ": usb_register failed %d\n",
256 		       ret);
257 		goto error_usb_register;
258 	}
259 
260 	printk(KERN_INFO KBUILD_MODNAME ":"
261 	       DRIVER_DESC ":" DRIVER_VERSION "\n");
262 
263 	memset(busid_table, 0, sizeof(busid_table));
264 	spin_lock_init(&busid_table_lock);
265 
266 	ret = driver_create_file(&stub_driver.drvwrap.driver,
267 				 &driver_attr_match_busid);
268 
269 	if (ret) {
270 		printk(KERN_ERR KBUILD_MODNAME ": create driver sysfs\n");
271 		goto error_create_file;
272 	}
273 
274 	return ret;
275 error_create_file:
276 	usb_deregister(&stub_driver);
277 error_usb_register:
278 	kmem_cache_destroy(stub_priv_cache);
279 	return ret;
280 }
281 
usb_stub_exit(void)282 static void __exit usb_stub_exit(void)
283 {
284 	driver_remove_file(&stub_driver.drvwrap.driver,
285 			   &driver_attr_match_busid);
286 
287 	/*
288 	 * deregister() calls stub_disconnect() for all devices. Device
289 	 * specific data is cleared in stub_disconnect().
290 	 */
291 	usb_deregister(&stub_driver);
292 
293 	kmem_cache_destroy(stub_priv_cache);
294 }
295 
296 module_init(usb_stub_init);
297 module_exit(usb_stub_exit);
298 
299 MODULE_AUTHOR(DRIVER_AUTHOR);
300 MODULE_DESCRIPTION(DRIVER_DESC);
301 MODULE_LICENSE("GPL");
302