162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2003-2008 Takahiro Hirofuchi 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/string.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/scatterlist.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "usbip_common.h" 1262306a36Sopenharmony_ci#include "stub.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define DRIVER_AUTHOR "Takahiro Hirofuchi" 1562306a36Sopenharmony_ci#define DRIVER_DESC "USB/IP Host Driver" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct kmem_cache *stub_priv_cache; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * busid_tables defines matching busids that usbip can grab. A user can change 2162306a36Sopenharmony_ci * dynamically what device is locally used and what device is exported to a 2262306a36Sopenharmony_ci * remote host. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define MAX_BUSID 16 2562306a36Sopenharmony_cistatic struct bus_id_priv busid_table[MAX_BUSID]; 2662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(busid_table_lock); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void init_busid_table(void) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci int i; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* 3362306a36Sopenharmony_ci * This also sets the bus_table[i].status to 3462306a36Sopenharmony_ci * STUB_BUSID_OTHER, which is 0. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci memset(busid_table, 0, sizeof(busid_table)); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci for (i = 0; i < MAX_BUSID; i++) 3962306a36Sopenharmony_ci spin_lock_init(&busid_table[i].busid_lock); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Find the index of the busid by name. 4462306a36Sopenharmony_ci * Must be called with busid_table_lock held. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic int get_busid_idx(const char *busid) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int i; 4962306a36Sopenharmony_ci int idx = -1; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci for (i = 0; i < MAX_BUSID; i++) { 5262306a36Sopenharmony_ci spin_lock(&busid_table[i].busid_lock); 5362306a36Sopenharmony_ci if (busid_table[i].name[0]) 5462306a36Sopenharmony_ci if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 5562306a36Sopenharmony_ci idx = i; 5662306a36Sopenharmony_ci spin_unlock(&busid_table[i].busid_lock); 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci spin_unlock(&busid_table[i].busid_lock); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci return idx; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Returns holding busid_lock. Should call put_busid_priv() to unlock */ 6562306a36Sopenharmony_cistruct bus_id_priv *get_busid_priv(const char *busid) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci int idx; 6862306a36Sopenharmony_ci struct bus_id_priv *bid = NULL; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spin_lock(&busid_table_lock); 7162306a36Sopenharmony_ci idx = get_busid_idx(busid); 7262306a36Sopenharmony_ci if (idx >= 0) { 7362306a36Sopenharmony_ci bid = &(busid_table[idx]); 7462306a36Sopenharmony_ci /* get busid_lock before returning */ 7562306a36Sopenharmony_ci spin_lock(&bid->busid_lock); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci spin_unlock(&busid_table_lock); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return bid; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_civoid put_busid_priv(struct bus_id_priv *bid) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (bid) 8562306a36Sopenharmony_ci spin_unlock(&bid->busid_lock); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int add_match_busid(char *busid) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int i; 9162306a36Sopenharmony_ci int ret = -1; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci spin_lock(&busid_table_lock); 9462306a36Sopenharmony_ci /* already registered? */ 9562306a36Sopenharmony_ci if (get_busid_idx(busid) >= 0) { 9662306a36Sopenharmony_ci ret = 0; 9762306a36Sopenharmony_ci goto out; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci for (i = 0; i < MAX_BUSID; i++) { 10162306a36Sopenharmony_ci spin_lock(&busid_table[i].busid_lock); 10262306a36Sopenharmony_ci if (!busid_table[i].name[0]) { 10362306a36Sopenharmony_ci strscpy(busid_table[i].name, busid, BUSID_SIZE); 10462306a36Sopenharmony_ci if ((busid_table[i].status != STUB_BUSID_ALLOC) && 10562306a36Sopenharmony_ci (busid_table[i].status != STUB_BUSID_REMOV)) 10662306a36Sopenharmony_ci busid_table[i].status = STUB_BUSID_ADDED; 10762306a36Sopenharmony_ci ret = 0; 10862306a36Sopenharmony_ci spin_unlock(&busid_table[i].busid_lock); 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci spin_unlock(&busid_table[i].busid_lock); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciout: 11562306a36Sopenharmony_ci spin_unlock(&busid_table_lock); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciint del_match_busid(char *busid) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int idx; 12362306a36Sopenharmony_ci int ret = -1; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci spin_lock(&busid_table_lock); 12662306a36Sopenharmony_ci idx = get_busid_idx(busid); 12762306a36Sopenharmony_ci if (idx < 0) 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* found */ 13162306a36Sopenharmony_ci ret = 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci spin_lock(&busid_table[idx].busid_lock); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (busid_table[idx].status == STUB_BUSID_OTHER) 13662306a36Sopenharmony_ci memset(busid_table[idx].name, 0, BUSID_SIZE); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if ((busid_table[idx].status != STUB_BUSID_OTHER) && 13962306a36Sopenharmony_ci (busid_table[idx].status != STUB_BUSID_ADDED)) 14062306a36Sopenharmony_ci busid_table[idx].status = STUB_BUSID_REMOV; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci spin_unlock(&busid_table[idx].busid_lock); 14362306a36Sopenharmony_ciout: 14462306a36Sopenharmony_ci spin_unlock(&busid_table_lock); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic ssize_t match_busid_show(struct device_driver *drv, char *buf) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int i; 15262306a36Sopenharmony_ci char *out = buf; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci spin_lock(&busid_table_lock); 15562306a36Sopenharmony_ci for (i = 0; i < MAX_BUSID; i++) { 15662306a36Sopenharmony_ci spin_lock(&busid_table[i].busid_lock); 15762306a36Sopenharmony_ci if (busid_table[i].name[0]) 15862306a36Sopenharmony_ci out += sprintf(out, "%s ", busid_table[i].name); 15962306a36Sopenharmony_ci spin_unlock(&busid_table[i].busid_lock); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci spin_unlock(&busid_table_lock); 16262306a36Sopenharmony_ci out += sprintf(out, "\n"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return out - buf; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic ssize_t match_busid_store(struct device_driver *dev, const char *buf, 16862306a36Sopenharmony_ci size_t count) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci char busid[BUSID_SIZE]; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (count < 5) 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* busid needs to include \0 termination */ 17662306a36Sopenharmony_ci if (strscpy(busid, buf + 4, BUSID_SIZE) < 0) 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!strncmp(buf, "add ", 4)) { 18062306a36Sopenharmony_ci if (add_match_busid(busid) < 0) 18162306a36Sopenharmony_ci return -ENOMEM; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci pr_debug("add busid %s\n", busid); 18462306a36Sopenharmony_ci return count; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!strncmp(buf, "del ", 4)) { 18862306a36Sopenharmony_ci if (del_match_busid(busid) < 0) 18962306a36Sopenharmony_ci return -ENODEV; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci pr_debug("del busid %s\n", busid); 19262306a36Sopenharmony_ci return count; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_cistatic DRIVER_ATTR_RW(match_busid); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int do_rebind(char *busid, struct bus_id_priv *busid_priv) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int ret = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* device_attach() callers should hold parent lock for USB */ 20462306a36Sopenharmony_ci if (busid_priv->udev->dev.parent) 20562306a36Sopenharmony_ci device_lock(busid_priv->udev->dev.parent); 20662306a36Sopenharmony_ci ret = device_attach(&busid_priv->udev->dev); 20762306a36Sopenharmony_ci if (busid_priv->udev->dev.parent) 20862306a36Sopenharmony_ci device_unlock(busid_priv->udev->dev.parent); 20962306a36Sopenharmony_ci if (ret < 0) 21062306a36Sopenharmony_ci dev_err(&busid_priv->udev->dev, "rebind failed\n"); 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void stub_device_rebind(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci#if IS_MODULE(CONFIG_USBIP_HOST) 21762306a36Sopenharmony_ci struct bus_id_priv *busid_priv; 21862306a36Sopenharmony_ci int i; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* update status to STUB_BUSID_OTHER so probe ignores the device */ 22162306a36Sopenharmony_ci spin_lock(&busid_table_lock); 22262306a36Sopenharmony_ci for (i = 0; i < MAX_BUSID; i++) { 22362306a36Sopenharmony_ci if (busid_table[i].name[0] && 22462306a36Sopenharmony_ci busid_table[i].shutdown_busid) { 22562306a36Sopenharmony_ci busid_priv = &(busid_table[i]); 22662306a36Sopenharmony_ci busid_priv->status = STUB_BUSID_OTHER; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci spin_unlock(&busid_table_lock); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* now run rebind - no need to hold locks. driver files are removed */ 23262306a36Sopenharmony_ci for (i = 0; i < MAX_BUSID; i++) { 23362306a36Sopenharmony_ci if (busid_table[i].name[0] && 23462306a36Sopenharmony_ci busid_table[i].shutdown_busid) { 23562306a36Sopenharmony_ci busid_priv = &(busid_table[i]); 23662306a36Sopenharmony_ci do_rebind(busid_table[i].name, busid_priv); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci#endif 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic ssize_t rebind_store(struct device_driver *dev, const char *buf, 24362306a36Sopenharmony_ci size_t count) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci int len; 24762306a36Sopenharmony_ci struct bus_id_priv *bid; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* buf length should be less that BUSID_SIZE */ 25062306a36Sopenharmony_ci len = strnlen(buf, BUSID_SIZE); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!(len < BUSID_SIZE)) 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci bid = get_busid_priv(buf); 25662306a36Sopenharmony_ci if (!bid) 25762306a36Sopenharmony_ci return -ENODEV; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* mark the device for deletion so probe ignores it during rescan */ 26062306a36Sopenharmony_ci bid->status = STUB_BUSID_OTHER; 26162306a36Sopenharmony_ci /* release the busid lock */ 26262306a36Sopenharmony_ci put_busid_priv(bid); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = do_rebind((char *) buf, bid); 26562306a36Sopenharmony_ci if (ret < 0) 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* delete device from busid_table */ 26962306a36Sopenharmony_ci del_match_busid((char *) buf); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return count; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic DRIVER_ATTR_WO(rebind); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct stub_priv *priv, *tmp; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci list_for_each_entry_safe(priv, tmp, listhead, list) { 28162306a36Sopenharmony_ci list_del_init(&priv->list); 28262306a36Sopenharmony_ci return priv; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return NULL; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_civoid stub_free_priv_and_urb(struct stub_priv *priv) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct urb *urb; 29162306a36Sopenharmony_ci int i; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci for (i = 0; i < priv->num_urbs; i++) { 29462306a36Sopenharmony_ci urb = priv->urbs[i]; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (!urb) 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci kfree(urb->setup_packet); 30062306a36Sopenharmony_ci urb->setup_packet = NULL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (urb->transfer_buffer && !priv->sgl) { 30462306a36Sopenharmony_ci kfree(urb->transfer_buffer); 30562306a36Sopenharmony_ci urb->transfer_buffer = NULL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (urb->num_sgs) { 30962306a36Sopenharmony_ci sgl_free(urb->sg); 31062306a36Sopenharmony_ci urb->sg = NULL; 31162306a36Sopenharmony_ci urb->num_sgs = 0; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci usb_free_urb(urb); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (!list_empty(&priv->list)) 31762306a36Sopenharmony_ci list_del(&priv->list); 31862306a36Sopenharmony_ci if (priv->sgl) 31962306a36Sopenharmony_ci sgl_free(priv->sgl); 32062306a36Sopenharmony_ci kfree(priv->urbs); 32162306a36Sopenharmony_ci kmem_cache_free(stub_priv_cache, priv); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic struct stub_priv *stub_priv_pop(struct stub_device *sdev) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci unsigned long flags; 32762306a36Sopenharmony_ci struct stub_priv *priv; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci spin_lock_irqsave(&sdev->priv_lock, flags); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci priv = stub_priv_pop_from_listhead(&sdev->priv_init); 33262306a36Sopenharmony_ci if (priv) 33362306a36Sopenharmony_ci goto done; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci priv = stub_priv_pop_from_listhead(&sdev->priv_tx); 33662306a36Sopenharmony_ci if (priv) 33762306a36Sopenharmony_ci goto done; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci priv = stub_priv_pop_from_listhead(&sdev->priv_free); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cidone: 34262306a36Sopenharmony_ci spin_unlock_irqrestore(&sdev->priv_lock, flags); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return priv; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_civoid stub_device_cleanup_urbs(struct stub_device *sdev) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct stub_priv *priv; 35062306a36Sopenharmony_ci int i; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n"); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci while ((priv = stub_priv_pop(sdev))) { 35562306a36Sopenharmony_ci for (i = 0; i < priv->num_urbs; i++) 35662306a36Sopenharmony_ci usb_kill_urb(priv->urbs[i]); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci stub_free_priv_and_urb(priv); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int __init usbip_host_init(void) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci int ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci init_busid_table(); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); 36962306a36Sopenharmony_ci if (!stub_priv_cache) { 37062306a36Sopenharmony_ci pr_err("kmem_cache_create failed\n"); 37162306a36Sopenharmony_ci return -ENOMEM; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = usb_register_device_driver(&stub_driver, THIS_MODULE); 37562306a36Sopenharmony_ci if (ret) { 37662306a36Sopenharmony_ci pr_err("usb_register failed %d\n", ret); 37762306a36Sopenharmony_ci goto err_usb_register; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ret = driver_create_file(&stub_driver.drvwrap.driver, 38162306a36Sopenharmony_ci &driver_attr_match_busid); 38262306a36Sopenharmony_ci if (ret) { 38362306a36Sopenharmony_ci pr_err("driver_create_file failed\n"); 38462306a36Sopenharmony_ci goto err_create_file; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = driver_create_file(&stub_driver.drvwrap.driver, 38862306a36Sopenharmony_ci &driver_attr_rebind); 38962306a36Sopenharmony_ci if (ret) { 39062306a36Sopenharmony_ci pr_err("driver_create_file failed\n"); 39162306a36Sopenharmony_ci goto err_create_file; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cierr_create_file: 39762306a36Sopenharmony_ci usb_deregister_device_driver(&stub_driver); 39862306a36Sopenharmony_cierr_usb_register: 39962306a36Sopenharmony_ci kmem_cache_destroy(stub_priv_cache); 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic void __exit usbip_host_exit(void) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci driver_remove_file(&stub_driver.drvwrap.driver, 40662306a36Sopenharmony_ci &driver_attr_match_busid); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci driver_remove_file(&stub_driver.drvwrap.driver, 40962306a36Sopenharmony_ci &driver_attr_rebind); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * deregister() calls stub_disconnect() for all devices. Device 41362306a36Sopenharmony_ci * specific data is cleared in stub_disconnect(). 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci usb_deregister_device_driver(&stub_driver); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* initiate scan to attach devices */ 41862306a36Sopenharmony_ci stub_device_rebind(); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci kmem_cache_destroy(stub_priv_cache); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cimodule_init(usbip_host_init); 42462306a36Sopenharmony_cimodule_exit(usbip_host_exit); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 42762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 42862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 429