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