18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2003-2008 Takahiro Hirofuchi
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/string.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "usbip_common.h"
128c2ecf20Sopenharmony_ci#include "stub.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Takahiro Hirofuchi"
158c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB/IP Host Driver"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct kmem_cache *stub_priv_cache;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * busid_tables defines matching busids that usbip can grab. A user can change
218c2ecf20Sopenharmony_ci * dynamically what device is locally used and what device is exported to a
228c2ecf20Sopenharmony_ci * remote host.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci#define MAX_BUSID 16
258c2ecf20Sopenharmony_cistatic struct bus_id_priv busid_table[MAX_BUSID];
268c2ecf20Sopenharmony_cistatic spinlock_t busid_table_lock;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void init_busid_table(void)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int i;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	/*
338c2ecf20Sopenharmony_ci	 * This also sets the bus_table[i].status to
348c2ecf20Sopenharmony_ci	 * STUB_BUSID_OTHER, which is 0.
358c2ecf20Sopenharmony_ci	 */
368c2ecf20Sopenharmony_ci	memset(busid_table, 0, sizeof(busid_table));
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	spin_lock_init(&busid_table_lock);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BUSID; i++)
418c2ecf20Sopenharmony_ci		spin_lock_init(&busid_table[i].busid_lock);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * Find the index of the busid by name.
468c2ecf20Sopenharmony_ci * Must be called with busid_table_lock held.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistatic int get_busid_idx(const char *busid)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	int i;
518c2ecf20Sopenharmony_ci	int idx = -1;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BUSID; i++) {
548c2ecf20Sopenharmony_ci		spin_lock(&busid_table[i].busid_lock);
558c2ecf20Sopenharmony_ci		if (busid_table[i].name[0])
568c2ecf20Sopenharmony_ci			if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
578c2ecf20Sopenharmony_ci				idx = i;
588c2ecf20Sopenharmony_ci				spin_unlock(&busid_table[i].busid_lock);
598c2ecf20Sopenharmony_ci				break;
608c2ecf20Sopenharmony_ci			}
618c2ecf20Sopenharmony_ci		spin_unlock(&busid_table[i].busid_lock);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	return idx;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
678c2ecf20Sopenharmony_cistruct bus_id_priv *get_busid_priv(const char *busid)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int idx;
708c2ecf20Sopenharmony_ci	struct bus_id_priv *bid = NULL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	spin_lock(&busid_table_lock);
738c2ecf20Sopenharmony_ci	idx = get_busid_idx(busid);
748c2ecf20Sopenharmony_ci	if (idx >= 0) {
758c2ecf20Sopenharmony_ci		bid = &(busid_table[idx]);
768c2ecf20Sopenharmony_ci		/* get busid_lock before returning */
778c2ecf20Sopenharmony_ci		spin_lock(&bid->busid_lock);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	spin_unlock(&busid_table_lock);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return bid;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_civoid put_busid_priv(struct bus_id_priv *bid)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	if (bid)
878c2ecf20Sopenharmony_ci		spin_unlock(&bid->busid_lock);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int add_match_busid(char *busid)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	int i;
938c2ecf20Sopenharmony_ci	int ret = -1;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	spin_lock(&busid_table_lock);
968c2ecf20Sopenharmony_ci	/* already registered? */
978c2ecf20Sopenharmony_ci	if (get_busid_idx(busid) >= 0) {
988c2ecf20Sopenharmony_ci		ret = 0;
998c2ecf20Sopenharmony_ci		goto out;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BUSID; i++) {
1038c2ecf20Sopenharmony_ci		spin_lock(&busid_table[i].busid_lock);
1048c2ecf20Sopenharmony_ci		if (!busid_table[i].name[0]) {
1058c2ecf20Sopenharmony_ci			strlcpy(busid_table[i].name, busid, BUSID_SIZE);
1068c2ecf20Sopenharmony_ci			if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
1078c2ecf20Sopenharmony_ci			    (busid_table[i].status != STUB_BUSID_REMOV))
1088c2ecf20Sopenharmony_ci				busid_table[i].status = STUB_BUSID_ADDED;
1098c2ecf20Sopenharmony_ci			ret = 0;
1108c2ecf20Sopenharmony_ci			spin_unlock(&busid_table[i].busid_lock);
1118c2ecf20Sopenharmony_ci			break;
1128c2ecf20Sopenharmony_ci		}
1138c2ecf20Sopenharmony_ci		spin_unlock(&busid_table[i].busid_lock);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciout:
1178c2ecf20Sopenharmony_ci	spin_unlock(&busid_table_lock);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return ret;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciint del_match_busid(char *busid)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int idx;
1258c2ecf20Sopenharmony_ci	int ret = -1;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	spin_lock(&busid_table_lock);
1288c2ecf20Sopenharmony_ci	idx = get_busid_idx(busid);
1298c2ecf20Sopenharmony_ci	if (idx < 0)
1308c2ecf20Sopenharmony_ci		goto out;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* found */
1338c2ecf20Sopenharmony_ci	ret = 0;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	spin_lock(&busid_table[idx].busid_lock);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (busid_table[idx].status == STUB_BUSID_OTHER)
1388c2ecf20Sopenharmony_ci		memset(busid_table[idx].name, 0, BUSID_SIZE);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
1418c2ecf20Sopenharmony_ci	    (busid_table[idx].status != STUB_BUSID_ADDED))
1428c2ecf20Sopenharmony_ci		busid_table[idx].status = STUB_BUSID_REMOV;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	spin_unlock(&busid_table[idx].busid_lock);
1458c2ecf20Sopenharmony_ciout:
1468c2ecf20Sopenharmony_ci	spin_unlock(&busid_table_lock);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return ret;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic ssize_t match_busid_show(struct device_driver *drv, char *buf)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	int i;
1548c2ecf20Sopenharmony_ci	char *out = buf;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	spin_lock(&busid_table_lock);
1578c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BUSID; i++) {
1588c2ecf20Sopenharmony_ci		spin_lock(&busid_table[i].busid_lock);
1598c2ecf20Sopenharmony_ci		if (busid_table[i].name[0])
1608c2ecf20Sopenharmony_ci			out += sprintf(out, "%s ", busid_table[i].name);
1618c2ecf20Sopenharmony_ci		spin_unlock(&busid_table[i].busid_lock);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	spin_unlock(&busid_table_lock);
1648c2ecf20Sopenharmony_ci	out += sprintf(out, "\n");
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return out - buf;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic ssize_t match_busid_store(struct device_driver *dev, const char *buf,
1708c2ecf20Sopenharmony_ci				 size_t count)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	int len;
1738c2ecf20Sopenharmony_ci	char busid[BUSID_SIZE];
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (count < 5)
1768c2ecf20Sopenharmony_ci		return -EINVAL;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* busid needs to include \0 termination */
1798c2ecf20Sopenharmony_ci	len = strlcpy(busid, buf + 4, BUSID_SIZE);
1808c2ecf20Sopenharmony_ci	if (sizeof(busid) <= len)
1818c2ecf20Sopenharmony_ci		return -EINVAL;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (!strncmp(buf, "add ", 4)) {
1848c2ecf20Sopenharmony_ci		if (add_match_busid(busid) < 0)
1858c2ecf20Sopenharmony_ci			return -ENOMEM;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		pr_debug("add busid %s\n", busid);
1888c2ecf20Sopenharmony_ci		return count;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (!strncmp(buf, "del ", 4)) {
1928c2ecf20Sopenharmony_ci		if (del_match_busid(busid) < 0)
1938c2ecf20Sopenharmony_ci			return -ENODEV;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		pr_debug("del busid %s\n", busid);
1968c2ecf20Sopenharmony_ci		return count;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return -EINVAL;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(match_busid);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int do_rebind(char *busid, struct bus_id_priv *busid_priv)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int ret = 0;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* device_attach() callers should hold parent lock for USB */
2088c2ecf20Sopenharmony_ci	if (busid_priv->udev->dev.parent)
2098c2ecf20Sopenharmony_ci		device_lock(busid_priv->udev->dev.parent);
2108c2ecf20Sopenharmony_ci	ret = device_attach(&busid_priv->udev->dev);
2118c2ecf20Sopenharmony_ci	if (busid_priv->udev->dev.parent)
2128c2ecf20Sopenharmony_ci		device_unlock(busid_priv->udev->dev.parent);
2138c2ecf20Sopenharmony_ci	if (ret < 0)
2148c2ecf20Sopenharmony_ci		dev_err(&busid_priv->udev->dev, "rebind failed\n");
2158c2ecf20Sopenharmony_ci	return ret;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void stub_device_rebind(void)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci#if IS_MODULE(CONFIG_USBIP_HOST)
2218c2ecf20Sopenharmony_ci	struct bus_id_priv *busid_priv;
2228c2ecf20Sopenharmony_ci	int i;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* update status to STUB_BUSID_OTHER so probe ignores the device */
2258c2ecf20Sopenharmony_ci	spin_lock(&busid_table_lock);
2268c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BUSID; i++) {
2278c2ecf20Sopenharmony_ci		if (busid_table[i].name[0] &&
2288c2ecf20Sopenharmony_ci		    busid_table[i].shutdown_busid) {
2298c2ecf20Sopenharmony_ci			busid_priv = &(busid_table[i]);
2308c2ecf20Sopenharmony_ci			busid_priv->status = STUB_BUSID_OTHER;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	spin_unlock(&busid_table_lock);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* now run rebind - no need to hold locks. driver files are removed */
2368c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BUSID; i++) {
2378c2ecf20Sopenharmony_ci		if (busid_table[i].name[0] &&
2388c2ecf20Sopenharmony_ci		    busid_table[i].shutdown_busid) {
2398c2ecf20Sopenharmony_ci			busid_priv = &(busid_table[i]);
2408c2ecf20Sopenharmony_ci			do_rebind(busid_table[i].name, busid_priv);
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci#endif
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic ssize_t rebind_store(struct device_driver *dev, const char *buf,
2478c2ecf20Sopenharmony_ci				 size_t count)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	int ret;
2508c2ecf20Sopenharmony_ci	int len;
2518c2ecf20Sopenharmony_ci	struct bus_id_priv *bid;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* buf length should be less that BUSID_SIZE */
2548c2ecf20Sopenharmony_ci	len = strnlen(buf, BUSID_SIZE);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (!(len < BUSID_SIZE))
2578c2ecf20Sopenharmony_ci		return -EINVAL;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	bid = get_busid_priv(buf);
2608c2ecf20Sopenharmony_ci	if (!bid)
2618c2ecf20Sopenharmony_ci		return -ENODEV;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* mark the device for deletion so probe ignores it during rescan */
2648c2ecf20Sopenharmony_ci	bid->status = STUB_BUSID_OTHER;
2658c2ecf20Sopenharmony_ci	/* release the busid lock */
2668c2ecf20Sopenharmony_ci	put_busid_priv(bid);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	ret = do_rebind((char *) buf, bid);
2698c2ecf20Sopenharmony_ci	if (ret < 0)
2708c2ecf20Sopenharmony_ci		return ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* delete device from busid_table */
2738c2ecf20Sopenharmony_ci	del_match_busid((char *) buf);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return count;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(rebind);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct stub_priv *priv, *tmp;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	list_for_each_entry_safe(priv, tmp, listhead, list) {
2858c2ecf20Sopenharmony_ci		list_del_init(&priv->list);
2868c2ecf20Sopenharmony_ci		return priv;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return NULL;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_civoid stub_free_priv_and_urb(struct stub_priv *priv)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct urb *urb;
2958c2ecf20Sopenharmony_ci	int i;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	for (i = 0; i < priv->num_urbs; i++) {
2988c2ecf20Sopenharmony_ci		urb = priv->urbs[i];
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		if (!urb)
3018c2ecf20Sopenharmony_ci			return;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		kfree(urb->setup_packet);
3048c2ecf20Sopenharmony_ci		urb->setup_packet = NULL;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		if (urb->transfer_buffer && !priv->sgl) {
3088c2ecf20Sopenharmony_ci			kfree(urb->transfer_buffer);
3098c2ecf20Sopenharmony_ci			urb->transfer_buffer = NULL;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		if (urb->num_sgs) {
3138c2ecf20Sopenharmony_ci			sgl_free(urb->sg);
3148c2ecf20Sopenharmony_ci			urb->sg = NULL;
3158c2ecf20Sopenharmony_ci			urb->num_sgs = 0;
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		usb_free_urb(urb);
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	if (!list_empty(&priv->list))
3218c2ecf20Sopenharmony_ci		list_del(&priv->list);
3228c2ecf20Sopenharmony_ci	if (priv->sgl)
3238c2ecf20Sopenharmony_ci		sgl_free(priv->sgl);
3248c2ecf20Sopenharmony_ci	kfree(priv->urbs);
3258c2ecf20Sopenharmony_ci	kmem_cache_free(stub_priv_cache, priv);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic struct stub_priv *stub_priv_pop(struct stub_device *sdev)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	unsigned long flags;
3318c2ecf20Sopenharmony_ci	struct stub_priv *priv;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sdev->priv_lock, flags);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	priv = stub_priv_pop_from_listhead(&sdev->priv_init);
3368c2ecf20Sopenharmony_ci	if (priv)
3378c2ecf20Sopenharmony_ci		goto done;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
3408c2ecf20Sopenharmony_ci	if (priv)
3418c2ecf20Sopenharmony_ci		goto done;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	priv = stub_priv_pop_from_listhead(&sdev->priv_free);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cidone:
3468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sdev->priv_lock, flags);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return priv;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_civoid stub_device_cleanup_urbs(struct stub_device *sdev)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct stub_priv *priv;
3548c2ecf20Sopenharmony_ci	int i;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n");
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	while ((priv = stub_priv_pop(sdev))) {
3598c2ecf20Sopenharmony_ci		for (i = 0; i < priv->num_urbs; i++)
3608c2ecf20Sopenharmony_ci			usb_kill_urb(priv->urbs[i]);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		stub_free_priv_and_urb(priv);
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int __init usbip_host_init(void)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	int ret;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	init_busid_table();
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN);
3738c2ecf20Sopenharmony_ci	if (!stub_priv_cache) {
3748c2ecf20Sopenharmony_ci		pr_err("kmem_cache_create failed\n");
3758c2ecf20Sopenharmony_ci		return -ENOMEM;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	ret = usb_register_device_driver(&stub_driver, THIS_MODULE);
3798c2ecf20Sopenharmony_ci	if (ret) {
3808c2ecf20Sopenharmony_ci		pr_err("usb_register failed %d\n", ret);
3818c2ecf20Sopenharmony_ci		goto err_usb_register;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	ret = driver_create_file(&stub_driver.drvwrap.driver,
3858c2ecf20Sopenharmony_ci				 &driver_attr_match_busid);
3868c2ecf20Sopenharmony_ci	if (ret) {
3878c2ecf20Sopenharmony_ci		pr_err("driver_create_file failed\n");
3888c2ecf20Sopenharmony_ci		goto err_create_file;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ret = driver_create_file(&stub_driver.drvwrap.driver,
3928c2ecf20Sopenharmony_ci				 &driver_attr_rebind);
3938c2ecf20Sopenharmony_ci	if (ret) {
3948c2ecf20Sopenharmony_ci		pr_err("driver_create_file failed\n");
3958c2ecf20Sopenharmony_ci		goto err_create_file;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return ret;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cierr_create_file:
4018c2ecf20Sopenharmony_ci	usb_deregister_device_driver(&stub_driver);
4028c2ecf20Sopenharmony_cierr_usb_register:
4038c2ecf20Sopenharmony_ci	kmem_cache_destroy(stub_priv_cache);
4048c2ecf20Sopenharmony_ci	return ret;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void __exit usbip_host_exit(void)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	driver_remove_file(&stub_driver.drvwrap.driver,
4108c2ecf20Sopenharmony_ci			   &driver_attr_match_busid);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	driver_remove_file(&stub_driver.drvwrap.driver,
4138c2ecf20Sopenharmony_ci			   &driver_attr_rebind);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/*
4168c2ecf20Sopenharmony_ci	 * deregister() calls stub_disconnect() for all devices. Device
4178c2ecf20Sopenharmony_ci	 * specific data is cleared in stub_disconnect().
4188c2ecf20Sopenharmony_ci	 */
4198c2ecf20Sopenharmony_ci	usb_deregister_device_driver(&stub_driver);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* initiate scan to attach devices */
4228c2ecf20Sopenharmony_ci	stub_device_rebind();
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	kmem_cache_destroy(stub_priv_cache);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cimodule_init(usbip_host_init);
4288c2ecf20Sopenharmony_cimodule_exit(usbip_host_exit);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
4318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
4328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
433