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