linux/drivers/usb/usbip/stub_main.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2003-2008 Takahiro Hirofuchi
   4 */
   5
   6#include <linux/string.h>
   7#include <linux/module.h>
   8#include <linux/device.h>
   9
  10#include "usbip_common.h"
  11#include "stub.h"
  12
  13#define DRIVER_AUTHOR "Takahiro Hirofuchi"
  14#define DRIVER_DESC "USB/IP Host Driver"
  15
  16struct kmem_cache *stub_priv_cache;
  17
  18/*
  19 * busid_tables defines matching busids that usbip can grab. A user can change
  20 * dynamically what device is locally used and what device is exported to a
  21 * remote host.
  22 */
  23#define MAX_BUSID 16
  24static struct bus_id_priv busid_table[MAX_BUSID];
  25static spinlock_t busid_table_lock;
  26
  27static void init_busid_table(void)
  28{
  29        int i;
  30
  31        /*
  32         * This also sets the bus_table[i].status to
  33         * STUB_BUSID_OTHER, which is 0.
  34         */
  35        memset(busid_table, 0, sizeof(busid_table));
  36
  37        spin_lock_init(&busid_table_lock);
  38
  39        for (i = 0; i < MAX_BUSID; i++)
  40                spin_lock_init(&busid_table[i].busid_lock);
  41}
  42
  43/*
  44 * Find the index of the busid by name.
  45 * Must be called with busid_table_lock held.
  46 */
  47static int get_busid_idx(const char *busid)
  48{
  49        int i;
  50        int idx = -1;
  51
  52        for (i = 0; i < MAX_BUSID; i++) {
  53                spin_lock(&busid_table[i].busid_lock);
  54                if (busid_table[i].name[0])
  55                        if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
  56                                idx = i;
  57                                spin_unlock(&busid_table[i].busid_lock);
  58                                break;
  59                        }
  60                spin_unlock(&busid_table[i].busid_lock);
  61        }
  62        return idx;
  63}
  64
  65/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
  66struct bus_id_priv *get_busid_priv(const char *busid)
  67{
  68        int idx;
  69        struct bus_id_priv *bid = NULL;
  70
  71        spin_lock(&busid_table_lock);
  72        idx = get_busid_idx(busid);
  73        if (idx >= 0) {
  74                bid = &(busid_table[idx]);
  75                /* get busid_lock before returning */
  76                spin_lock(&bid->busid_lock);
  77        }
  78        spin_unlock(&busid_table_lock);
  79
  80        return bid;
  81}
  82
  83void put_busid_priv(struct bus_id_priv *bid)
  84{
  85        if (bid)
  86                spin_unlock(&bid->busid_lock);
  87}
  88
  89static int add_match_busid(char *busid)
  90{
  91        int i;
  92        int ret = -1;
  93
  94        spin_lock(&busid_table_lock);
  95        /* already registered? */
  96        if (get_busid_idx(busid) >= 0) {
  97                ret = 0;
  98                goto out;
  99        }
 100
 101        for (i = 0; i < MAX_BUSID; i++) {
 102                spin_lock(&busid_table[i].busid_lock);
 103                if (!busid_table[i].name[0]) {
 104                        strlcpy(busid_table[i].name, busid, BUSID_SIZE);
 105                        if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
 106                            (busid_table[i].status != STUB_BUSID_REMOV))
 107                                busid_table[i].status = STUB_BUSID_ADDED;
 108                        ret = 0;
 109                        spin_unlock(&busid_table[i].busid_lock);
 110                        break;
 111                }
 112                spin_unlock(&busid_table[i].busid_lock);
 113        }
 114
 115out:
 116        spin_unlock(&busid_table_lock);
 117
 118        return ret;
 119}
 120
 121int del_match_busid(char *busid)
 122{
 123        int idx;
 124        int ret = -1;
 125
 126        spin_lock(&busid_table_lock);
 127        idx = get_busid_idx(busid);
 128        if (idx < 0)
 129                goto out;
 130
 131        /* found */
 132        ret = 0;
 133
 134        spin_lock(&busid_table[idx].busid_lock);
 135
 136        if (busid_table[idx].status == STUB_BUSID_OTHER)
 137                memset(busid_table[idx].name, 0, BUSID_SIZE);
 138
 139        if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
 140            (busid_table[idx].status != STUB_BUSID_ADDED))
 141                busid_table[idx].status = STUB_BUSID_REMOV;
 142
 143        spin_unlock(&busid_table[idx].busid_lock);
 144out:
 145        spin_unlock(&busid_table_lock);
 146
 147        return ret;
 148}
 149
 150static ssize_t match_busid_show(struct device_driver *drv, char *buf)
 151{
 152        int i;
 153        char *out = buf;
 154
 155        spin_lock(&busid_table_lock);
 156        for (i = 0; i < MAX_BUSID; i++) {
 157                spin_lock(&busid_table[i].busid_lock);
 158                if (busid_table[i].name[0])
 159                        out += sprintf(out, "%s ", busid_table[i].name);
 160                spin_unlock(&busid_table[i].busid_lock);
 161        }
 162        spin_unlock(&busid_table_lock);
 163        out += sprintf(out, "\n");
 164
 165        return out - buf;
 166}
 167
 168static ssize_t match_busid_store(struct device_driver *dev, const char *buf,
 169                                 size_t count)
 170{
 171        int len;
 172        char busid[BUSID_SIZE];
 173
 174        if (count < 5)
 175                return -EINVAL;
 176
 177        /* busid needs to include \0 termination */
 178        len = strlcpy(busid, buf + 4, BUSID_SIZE);
 179        if (sizeof(busid) <= len)
 180                return -EINVAL;
 181
 182        if (!strncmp(buf, "add ", 4)) {
 183                if (add_match_busid(busid) < 0)
 184                        return -ENOMEM;
 185
 186                pr_debug("add busid %s\n", busid);
 187                return count;
 188        }
 189
 190        if (!strncmp(buf, "del ", 4)) {
 191                if (del_match_busid(busid) < 0)
 192                        return -ENODEV;
 193
 194                pr_debug("del busid %s\n", busid);
 195                return count;
 196        }
 197
 198        return -EINVAL;
 199}
 200static DRIVER_ATTR_RW(match_busid);
 201
 202static int do_rebind(char *busid, struct bus_id_priv *busid_priv)
 203{
 204        int ret = 0;
 205
 206        /* device_attach() callers should hold parent lock for USB */
 207        if (busid_priv->udev->dev.parent)
 208                device_lock(busid_priv->udev->dev.parent);
 209        ret = device_attach(&busid_priv->udev->dev);
 210        if (busid_priv->udev->dev.parent)
 211                device_unlock(busid_priv->udev->dev.parent);
 212        if (ret < 0)
 213                dev_err(&busid_priv->udev->dev, "rebind failed\n");
 214        return ret;
 215}
 216
 217static void stub_device_rebind(void)
 218{
 219#if IS_MODULE(CONFIG_USBIP_HOST)
 220        struct bus_id_priv *busid_priv;
 221        int i;
 222
 223        /* update status to STUB_BUSID_OTHER so probe ignores the device */
 224        spin_lock(&busid_table_lock);
 225        for (i = 0; i < MAX_BUSID; i++) {
 226                if (busid_table[i].name[0] &&
 227                    busid_table[i].shutdown_busid) {
 228                        busid_priv = &(busid_table[i]);
 229                        busid_priv->status = STUB_BUSID_OTHER;
 230                }
 231        }
 232        spin_unlock(&busid_table_lock);
 233
 234        /* now run rebind - no need to hold locks. driver files are removed */
 235        for (i = 0; i < MAX_BUSID; i++) {
 236                if (busid_table[i].name[0] &&
 237                    busid_table[i].shutdown_busid) {
 238                        busid_priv = &(busid_table[i]);
 239                        do_rebind(busid_table[i].name, busid_priv);
 240                }
 241        }
 242#endif
 243}
 244
 245static ssize_t rebind_store(struct device_driver *dev, const char *buf,
 246                                 size_t count)
 247{
 248        int ret;
 249        int len;
 250        struct bus_id_priv *bid;
 251
 252        /* buf length should be less that BUSID_SIZE */
 253        len = strnlen(buf, BUSID_SIZE);
 254
 255        if (!(len < BUSID_SIZE))
 256                return -EINVAL;
 257
 258        bid = get_busid_priv(buf);
 259        if (!bid)
 260                return -ENODEV;
 261
 262        /* mark the device for deletion so probe ignores it during rescan */
 263        bid->status = STUB_BUSID_OTHER;
 264        /* release the busid lock */
 265        put_busid_priv(bid);
 266
 267        ret = do_rebind((char *) buf, bid);
 268        if (ret < 0)
 269                return ret;
 270
 271        /* delete device from busid_table */
 272        del_match_busid((char *) buf);
 273
 274        return count;
 275}
 276
 277static DRIVER_ATTR_WO(rebind);
 278
 279static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
 280{
 281        struct stub_priv *priv, *tmp;
 282
 283        list_for_each_entry_safe(priv, tmp, listhead, list) {
 284                list_del(&priv->list);
 285                return priv;
 286        }
 287
 288        return NULL;
 289}
 290
 291static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
 292{
 293        unsigned long flags;
 294        struct stub_priv *priv;
 295
 296        spin_lock_irqsave(&sdev->priv_lock, flags);
 297
 298        priv = stub_priv_pop_from_listhead(&sdev->priv_init);
 299        if (priv)
 300                goto done;
 301
 302        priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
 303        if (priv)
 304                goto done;
 305
 306        priv = stub_priv_pop_from_listhead(&sdev->priv_free);
 307
 308done:
 309        spin_unlock_irqrestore(&sdev->priv_lock, flags);
 310
 311        return priv;
 312}
 313
 314void stub_device_cleanup_urbs(struct stub_device *sdev)
 315{
 316        struct stub_priv *priv;
 317        struct urb *urb;
 318
 319        dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n");
 320
 321        while ((priv = stub_priv_pop(sdev))) {
 322                urb = priv->urb;
 323                dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n",
 324                        priv->seqnum);
 325                usb_kill_urb(urb);
 326
 327                kmem_cache_free(stub_priv_cache, priv);
 328
 329                kfree(urb->transfer_buffer);
 330                urb->transfer_buffer = NULL;
 331
 332                kfree(urb->setup_packet);
 333                urb->setup_packet = NULL;
 334
 335                usb_free_urb(urb);
 336        }
 337}
 338
 339static int __init usbip_host_init(void)
 340{
 341        int ret;
 342
 343        init_busid_table();
 344
 345        stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN);
 346        if (!stub_priv_cache) {
 347                pr_err("kmem_cache_create failed\n");
 348                return -ENOMEM;
 349        }
 350
 351        ret = usb_register_device_driver(&stub_driver, THIS_MODULE);
 352        if (ret) {
 353                pr_err("usb_register failed %d\n", ret);
 354                goto err_usb_register;
 355        }
 356
 357        ret = driver_create_file(&stub_driver.drvwrap.driver,
 358                                 &driver_attr_match_busid);
 359        if (ret) {
 360                pr_err("driver_create_file failed\n");
 361                goto err_create_file;
 362        }
 363
 364        ret = driver_create_file(&stub_driver.drvwrap.driver,
 365                                 &driver_attr_rebind);
 366        if (ret) {
 367                pr_err("driver_create_file failed\n");
 368                goto err_create_file;
 369        }
 370
 371        return ret;
 372
 373err_create_file:
 374        usb_deregister_device_driver(&stub_driver);
 375err_usb_register:
 376        kmem_cache_destroy(stub_priv_cache);
 377        return ret;
 378}
 379
 380static void __exit usbip_host_exit(void)
 381{
 382        driver_remove_file(&stub_driver.drvwrap.driver,
 383                           &driver_attr_match_busid);
 384
 385        driver_remove_file(&stub_driver.drvwrap.driver,
 386                           &driver_attr_rebind);
 387
 388        /*
 389         * deregister() calls stub_disconnect() for all devices. Device
 390         * specific data is cleared in stub_disconnect().
 391         */
 392        usb_deregister_device_driver(&stub_driver);
 393
 394        /* initiate scan to attach devices */
 395        stub_device_rebind();
 396
 397        kmem_cache_destroy(stub_priv_cache);
 398}
 399
 400module_init(usbip_host_init);
 401module_exit(usbip_host_exit);
 402
 403MODULE_AUTHOR(DRIVER_AUTHOR);
 404MODULE_DESCRIPTION(DRIVER_DESC);
 405MODULE_LICENSE("GPL");
 406