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#include <linux/slab.h> 21 22#include "usbip_common.h" 23#include "stub.h" 24 25/* Version Information */ 26#define DRIVER_VERSION "1.0" 27#define DRIVER_AUTHOR "Takahiro Hirofuchi" 28#define DRIVER_DESC "Stub Driver for USB/IP" 29 30/* stub_priv is allocated from stub_priv_cache */ 31struct kmem_cache *stub_priv_cache; 32 33/*-------------------------------------------------------------------------*/ 34 35/* Define sysfs entries for the usbip driver */ 36 37 38/* 39 * busid_tables defines matching busids that usbip can grab. A user can change 40 * dynamically what device is locally used and what device is exported to a 41 * remote host. 42 */ 43#define MAX_BUSID 16 44static struct bus_id_priv busid_table[MAX_BUSID]; 45static spinlock_t busid_table_lock; 46 47 48int 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].name[0]) 56 if (!strncmp(busid_table[i].name, 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 67struct bus_id_priv *get_busid_priv(const char *busid) 68{ 69 int i; 70 71 spin_lock(&busid_table_lock); 72 73 for (i = 0; i < MAX_BUSID; i++) 74 if (busid_table[i].name[0]) 75 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 76 /* already registerd */ 77 spin_unlock(&busid_table_lock); 78 return &(busid_table[i]); 79 } 80 81 spin_unlock(&busid_table_lock); 82 83 return NULL; 84} 85 86static ssize_t show_match_busid(struct device_driver *drv, char *buf) 87{ 88 int i; 89 char *out = buf; 90 91 spin_lock(&busid_table_lock); 92 93 for (i = 0; i < MAX_BUSID; i++) 94 if (busid_table[i].name[0]) 95 out += sprintf(out, "%s ", busid_table[i].name); 96 97 spin_unlock(&busid_table_lock); 98 99 out += sprintf(out, "\n"); 100 101 return out - buf; 102} 103 104static int add_match_busid(char *busid) 105{ 106 int i; 107 108 if (!match_busid(busid)) 109 return 0; 110 111 spin_lock(&busid_table_lock); 112 113 for (i = 0; i < MAX_BUSID; i++) 114 if (!busid_table[i].name[0]) { 115 strncpy(busid_table[i].name, busid, BUSID_SIZE); 116 if ((busid_table[i].status != STUB_BUSID_ALLOC) && 117 (busid_table[i].status != STUB_BUSID_REMOV)) 118 busid_table[i].status = STUB_BUSID_ADDED; 119 spin_unlock(&busid_table_lock); 120 return 0; 121 } 122 123 spin_unlock(&busid_table_lock); 124 125 return -1; 126} 127 128int del_match_busid(char *busid) 129{ 130 int i; 131 132 spin_lock(&busid_table_lock); 133 134 for (i = 0; i < MAX_BUSID; i++) 135 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 136 /* found */ 137 if (busid_table[i].status == STUB_BUSID_OTHER) 138 memset(busid_table[i].name, 0, BUSID_SIZE); 139 if ((busid_table[i].status != STUB_BUSID_OTHER) && 140 (busid_table[i].status != STUB_BUSID_ADDED)) { 141 busid_table[i].status = STUB_BUSID_REMOV; 142 } 143 spin_unlock(&busid_table_lock); 144 return 0; 145 } 146 147 spin_unlock(&busid_table_lock); 148 149 return -1; 150} 151static void init_busid_table(void) 152{ 153 int i; 154 155 156 for (i = 0; i < MAX_BUSID; i++) { 157 memset(busid_table[i].name, 0, BUSID_SIZE); 158 busid_table[i].status = STUB_BUSID_OTHER; 159 busid_table[i].interf_count = 0; 160 busid_table[i].sdev = NULL; 161 busid_table[i].shutdown_busid = 0; 162 } 163 spin_lock_init(&busid_table_lock); 164} 165 166static ssize_t store_match_busid(struct device_driver *dev, const char *buf, 167 size_t count) 168{ 169 int len; 170 char busid[BUSID_SIZE]; 171 172 if (count < 5) 173 return -EINVAL; 174 175 /* strnlen() does not include \0 */ 176 len = strnlen(buf + 4, BUSID_SIZE); 177 178 /* busid needs to include \0 termination */ 179 if (!(len < BUSID_SIZE)) 180 return -EINVAL; 181 182 strncpy(busid, buf + 4, BUSID_SIZE); 183 184 185 if (!strncmp(buf, "add ", 4)) { 186 if (add_match_busid(busid) < 0) 187 return -ENOMEM; 188 else { 189 usbip_udbg("add busid %s\n", busid); 190 return count; 191 } 192 } else if (!strncmp(buf, "del ", 4)) { 193 if (del_match_busid(busid) < 0) 194 return -ENODEV; 195 else { 196 usbip_udbg("del busid %s\n", busid); 197 return count; 198 } 199 } else 200 return -EINVAL; 201} 202 203static DRIVER_ATTR(match_busid, S_IRUSR|S_IWUSR, show_match_busid, 204 store_match_busid); 205 206 207 208/*-------------------------------------------------------------------------*/ 209 210/* Cleanup functions used to free private data */ 211 212static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) 213{ 214 struct stub_priv *priv, *tmp; 215 216 list_for_each_entry_safe(priv, tmp, listhead, list) { 217 list_del(&priv->list); 218 return priv; 219 } 220 221 return NULL; 222} 223 224static struct stub_priv *stub_priv_pop(struct stub_device *sdev) 225{ 226 unsigned long flags; 227 struct stub_priv *priv; 228 229 spin_lock_irqsave(&sdev->priv_lock, flags); 230 231 priv = stub_priv_pop_from_listhead(&sdev->priv_init); 232 if (priv) { 233 spin_unlock_irqrestore(&sdev->priv_lock, flags); 234 return priv; 235 } 236 237 priv = stub_priv_pop_from_listhead(&sdev->priv_tx); 238 if (priv) { 239 spin_unlock_irqrestore(&sdev->priv_lock, flags); 240 return priv; 241 } 242 243 priv = stub_priv_pop_from_listhead(&sdev->priv_free); 244 if (priv) { 245 spin_unlock_irqrestore(&sdev->priv_lock, flags); 246 return priv; 247 } 248 249 spin_unlock_irqrestore(&sdev->priv_lock, flags); 250 return NULL; 251} 252 253void stub_device_cleanup_urbs(struct stub_device *sdev) 254{ 255 struct stub_priv *priv; 256 257 usbip_udbg("free sdev %p\n", sdev); 258 259 while ((priv = stub_priv_pop(sdev))) { 260 struct urb *urb = priv->urb; 261 262 usbip_udbg(" free urb %p\n", urb); 263 usb_kill_urb(urb); 264 265 kmem_cache_free(stub_priv_cache, priv); 266 267 if (urb->transfer_buffer != NULL) 268 kfree(urb->transfer_buffer); 269 270 if (urb->setup_packet != NULL) 271 kfree(urb->setup_packet); 272 273 usb_free_urb(urb); 274 } 275} 276 277 278/*-------------------------------------------------------------------------*/ 279 280static int __init usb_stub_init(void) 281{ 282 int ret; 283 284 stub_priv_cache = kmem_cache_create("stub_priv", 285 sizeof(struct stub_priv), 0, 286 SLAB_HWCACHE_ALIGN, NULL); 287 288 if (!stub_priv_cache) { 289 printk(KERN_ERR KBUILD_MODNAME 290 ": create stub_priv_cache error\n"); 291 return -ENOMEM; 292 } 293 294 ret = usb_register(&stub_driver); 295 if (ret) { 296 printk(KERN_ERR KBUILD_MODNAME ": usb_register failed %d\n", 297 ret); 298 goto error_usb_register; 299 } 300 301 printk(KERN_INFO KBUILD_MODNAME ":" 302 DRIVER_DESC ":" DRIVER_VERSION "\n"); 303 304 init_busid_table(); 305 306 ret = driver_create_file(&stub_driver.drvwrap.driver, 307 &driver_attr_match_busid); 308 309 if (ret) { 310 printk(KERN_ERR KBUILD_MODNAME ": create driver sysfs\n"); 311 goto error_create_file; 312 } 313 314 return ret; 315error_create_file: 316 usb_deregister(&stub_driver); 317error_usb_register: 318 kmem_cache_destroy(stub_priv_cache); 319 return ret; 320} 321 322static void __exit usb_stub_exit(void) 323{ 324 driver_remove_file(&stub_driver.drvwrap.driver, 325 &driver_attr_match_busid); 326 327 /* 328 * deregister() calls stub_disconnect() for all devices. Device 329 * specific data is cleared in stub_disconnect(). 330 */ 331 usb_deregister(&stub_driver); 332 333 kmem_cache_destroy(stub_priv_cache); 334} 335 336module_init(usb_stub_init); 337module_exit(usb_stub_exit); 338 339MODULE_AUTHOR(DRIVER_AUTHOR); 340MODULE_DESCRIPTION(DRIVER_DESC); 341MODULE_LICENSE("GPL"); 342