linux/drivers/staging/usbip/stub_main.c
<<
>>
Prefs
   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 */
  30struct 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
  44static char busid_table[MAX_BUSID][BUSID_SIZE];
  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][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
  67static 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
  85static 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
 106static 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
 125static 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                        usbip_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                        usbip_udbg("del busid %s\n", busid);
 156                        return count;
 157                }
 158        } else
 159                return -EINVAL;
 160}
 161
 162static 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
 171static 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
 183static 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
 212void stub_device_cleanup_urbs(struct stub_device *sdev)
 213{
 214        struct stub_priv *priv;
 215
 216        usbip_udbg("free sdev %p\n", sdev);
 217
 218        while ((priv = stub_priv_pop(sdev))) {
 219                struct urb *urb = priv->urb;
 220
 221                usbip_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
 239static 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;
 275error_create_file:
 276        usb_deregister(&stub_driver);
 277error_usb_register:
 278        kmem_cache_destroy(stub_priv_cache);
 279        return ret;
 280}
 281
 282static 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
 296module_init(usb_stub_init);
 297module_exit(usb_stub_exit);
 298
 299MODULE_AUTHOR(DRIVER_AUTHOR);
 300MODULE_DESCRIPTION(DRIVER_DESC);
 301MODULE_LICENSE("GPL");
 302