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/device.h>
762306a36Sopenharmony_ci#include <linux/file.h>
862306a36Sopenharmony_ci#include <linux/kthread.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "usbip_common.h"
1262306a36Sopenharmony_ci#include "stub.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * usbip_status shows the status of usbip-host as long as this driver is bound
1662306a36Sopenharmony_ci * to the target device.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_cistatic ssize_t usbip_status_show(struct device *dev,
1962306a36Sopenharmony_ci				 struct device_attribute *attr, char *buf)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct stub_device *sdev = dev_get_drvdata(dev);
2262306a36Sopenharmony_ci	int status;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (!sdev) {
2562306a36Sopenharmony_ci		dev_err(dev, "sdev is null\n");
2662306a36Sopenharmony_ci		return -ENODEV;
2762306a36Sopenharmony_ci	}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	spin_lock_irq(&sdev->ud.lock);
3062306a36Sopenharmony_ci	status = sdev->ud.status;
3162306a36Sopenharmony_ci	spin_unlock_irq(&sdev->ud.lock);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", status);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(usbip_status);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * usbip_sockfd gets a socket descriptor of an established TCP connection that
3962306a36Sopenharmony_ci * is used to transfer usbip requests by kernel threads. -1 is a magic number
4062306a36Sopenharmony_ci * by which usbip connection is finished.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistatic ssize_t usbip_sockfd_store(struct device *dev, struct device_attribute *attr,
4362306a36Sopenharmony_ci			    const char *buf, size_t count)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct stub_device *sdev = dev_get_drvdata(dev);
4662306a36Sopenharmony_ci	int sockfd = 0;
4762306a36Sopenharmony_ci	struct socket *socket;
4862306a36Sopenharmony_ci	int rv;
4962306a36Sopenharmony_ci	struct task_struct *tcp_rx = NULL;
5062306a36Sopenharmony_ci	struct task_struct *tcp_tx = NULL;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!sdev) {
5362306a36Sopenharmony_ci		dev_err(dev, "sdev is null\n");
5462306a36Sopenharmony_ci		return -ENODEV;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	rv = sscanf(buf, "%d", &sockfd);
5862306a36Sopenharmony_ci	if (rv != 1)
5962306a36Sopenharmony_ci		return -EINVAL;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (sockfd != -1) {
6262306a36Sopenharmony_ci		int err;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		dev_info(dev, "stub up\n");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		mutex_lock(&sdev->ud.sysfs_lock);
6762306a36Sopenharmony_ci		spin_lock_irq(&sdev->ud.lock);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		if (sdev->ud.status != SDEV_ST_AVAILABLE) {
7062306a36Sopenharmony_ci			dev_err(dev, "not ready\n");
7162306a36Sopenharmony_ci			goto err;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		socket = sockfd_lookup(sockfd, &err);
7562306a36Sopenharmony_ci		if (!socket) {
7662306a36Sopenharmony_ci			dev_err(dev, "failed to lookup sock");
7762306a36Sopenharmony_ci			goto err;
7862306a36Sopenharmony_ci		}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (socket->type != SOCK_STREAM) {
8162306a36Sopenharmony_ci			dev_err(dev, "Expecting SOCK_STREAM - found %d",
8262306a36Sopenharmony_ci				socket->type);
8362306a36Sopenharmony_ci			goto sock_err;
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/* unlock and create threads and get tasks */
8762306a36Sopenharmony_ci		spin_unlock_irq(&sdev->ud.lock);
8862306a36Sopenharmony_ci		tcp_rx = kthread_create(stub_rx_loop, &sdev->ud, "stub_rx");
8962306a36Sopenharmony_ci		if (IS_ERR(tcp_rx)) {
9062306a36Sopenharmony_ci			sockfd_put(socket);
9162306a36Sopenharmony_ci			goto unlock_mutex;
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci		tcp_tx = kthread_create(stub_tx_loop, &sdev->ud, "stub_tx");
9462306a36Sopenharmony_ci		if (IS_ERR(tcp_tx)) {
9562306a36Sopenharmony_ci			kthread_stop(tcp_rx);
9662306a36Sopenharmony_ci			sockfd_put(socket);
9762306a36Sopenharmony_ci			goto unlock_mutex;
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		/* get task structs now */
10162306a36Sopenharmony_ci		get_task_struct(tcp_rx);
10262306a36Sopenharmony_ci		get_task_struct(tcp_tx);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		/* lock and update sdev->ud state */
10562306a36Sopenharmony_ci		spin_lock_irq(&sdev->ud.lock);
10662306a36Sopenharmony_ci		sdev->ud.tcp_socket = socket;
10762306a36Sopenharmony_ci		sdev->ud.sockfd = sockfd;
10862306a36Sopenharmony_ci		sdev->ud.tcp_rx = tcp_rx;
10962306a36Sopenharmony_ci		sdev->ud.tcp_tx = tcp_tx;
11062306a36Sopenharmony_ci		sdev->ud.status = SDEV_ST_USED;
11162306a36Sopenharmony_ci		spin_unlock_irq(&sdev->ud.lock);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		wake_up_process(sdev->ud.tcp_rx);
11462306a36Sopenharmony_ci		wake_up_process(sdev->ud.tcp_tx);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		mutex_unlock(&sdev->ud.sysfs_lock);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	} else {
11962306a36Sopenharmony_ci		dev_info(dev, "stub down\n");
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		mutex_lock(&sdev->ud.sysfs_lock);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		spin_lock_irq(&sdev->ud.lock);
12462306a36Sopenharmony_ci		if (sdev->ud.status != SDEV_ST_USED)
12562306a36Sopenharmony_ci			goto err;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		spin_unlock_irq(&sdev->ud.lock);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
13062306a36Sopenharmony_ci		mutex_unlock(&sdev->ud.sysfs_lock);
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return count;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cisock_err:
13662306a36Sopenharmony_ci	sockfd_put(socket);
13762306a36Sopenharmony_cierr:
13862306a36Sopenharmony_ci	spin_unlock_irq(&sdev->ud.lock);
13962306a36Sopenharmony_ciunlock_mutex:
14062306a36Sopenharmony_ci	mutex_unlock(&sdev->ud.sysfs_lock);
14162306a36Sopenharmony_ci	return -EINVAL;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_cistatic DEVICE_ATTR_WO(usbip_sockfd);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct attribute *usbip_attrs[] = {
14662306a36Sopenharmony_ci	&dev_attr_usbip_status.attr,
14762306a36Sopenharmony_ci	&dev_attr_usbip_sockfd.attr,
14862306a36Sopenharmony_ci	&dev_attr_usbip_debug.attr,
14962306a36Sopenharmony_ci	NULL,
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ciATTRIBUTE_GROUPS(usbip);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void stub_shutdown_connection(struct usbip_device *ud)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/*
15862306a36Sopenharmony_ci	 * When removing an exported device, kernel panic sometimes occurred
15962306a36Sopenharmony_ci	 * and then EIP was sk_wait_data of stub_rx thread. Is this because
16062306a36Sopenharmony_ci	 * sk_wait_data returned though stub_rx thread was already finished by
16162306a36Sopenharmony_ci	 * step 1?
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	if (ud->tcp_socket) {
16462306a36Sopenharmony_ci		dev_dbg(&sdev->udev->dev, "shutdown sockfd %d\n", ud->sockfd);
16562306a36Sopenharmony_ci		kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* 1. stop threads */
16962306a36Sopenharmony_ci	if (ud->tcp_rx) {
17062306a36Sopenharmony_ci		kthread_stop_put(ud->tcp_rx);
17162306a36Sopenharmony_ci		ud->tcp_rx = NULL;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	if (ud->tcp_tx) {
17462306a36Sopenharmony_ci		kthread_stop_put(ud->tcp_tx);
17562306a36Sopenharmony_ci		ud->tcp_tx = NULL;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * 2. close the socket
18062306a36Sopenharmony_ci	 *
18162306a36Sopenharmony_ci	 * tcp_socket is freed after threads are killed so that usbip_xmit does
18262306a36Sopenharmony_ci	 * not touch NULL socket.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	if (ud->tcp_socket) {
18562306a36Sopenharmony_ci		sockfd_put(ud->tcp_socket);
18662306a36Sopenharmony_ci		ud->tcp_socket = NULL;
18762306a36Sopenharmony_ci		ud->sockfd = -1;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* 3. free used data */
19162306a36Sopenharmony_ci	stub_device_cleanup_urbs(sdev);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* 4. free stub_unlink */
19462306a36Sopenharmony_ci	{
19562306a36Sopenharmony_ci		unsigned long flags;
19662306a36Sopenharmony_ci		struct stub_unlink *unlink, *tmp;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		spin_lock_irqsave(&sdev->priv_lock, flags);
19962306a36Sopenharmony_ci		list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
20062306a36Sopenharmony_ci			list_del(&unlink->list);
20162306a36Sopenharmony_ci			kfree(unlink);
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci		list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free,
20462306a36Sopenharmony_ci					 list) {
20562306a36Sopenharmony_ci			list_del(&unlink->list);
20662306a36Sopenharmony_ci			kfree(unlink);
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci		spin_unlock_irqrestore(&sdev->priv_lock, flags);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void stub_device_reset(struct usbip_device *ud)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
21562306a36Sopenharmony_ci	struct usb_device *udev = sdev->udev;
21662306a36Sopenharmony_ci	int ret;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	dev_dbg(&udev->dev, "device reset");
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	ret = usb_lock_device_for_reset(udev, NULL);
22162306a36Sopenharmony_ci	if (ret < 0) {
22262306a36Sopenharmony_ci		dev_err(&udev->dev, "lock for reset\n");
22362306a36Sopenharmony_ci		spin_lock_irq(&ud->lock);
22462306a36Sopenharmony_ci		ud->status = SDEV_ST_ERROR;
22562306a36Sopenharmony_ci		spin_unlock_irq(&ud->lock);
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* try to reset the device */
23062306a36Sopenharmony_ci	ret = usb_reset_device(udev);
23162306a36Sopenharmony_ci	usb_unlock_device(udev);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	spin_lock_irq(&ud->lock);
23462306a36Sopenharmony_ci	if (ret) {
23562306a36Sopenharmony_ci		dev_err(&udev->dev, "device reset\n");
23662306a36Sopenharmony_ci		ud->status = SDEV_ST_ERROR;
23762306a36Sopenharmony_ci	} else {
23862306a36Sopenharmony_ci		dev_info(&udev->dev, "device reset\n");
23962306a36Sopenharmony_ci		ud->status = SDEV_ST_AVAILABLE;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	spin_unlock_irq(&ud->lock);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void stub_device_unusable(struct usbip_device *ud)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	spin_lock_irq(&ud->lock);
24762306a36Sopenharmony_ci	ud->status = SDEV_ST_ERROR;
24862306a36Sopenharmony_ci	spin_unlock_irq(&ud->lock);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/**
25262306a36Sopenharmony_ci * stub_device_alloc - allocate a new stub_device struct
25362306a36Sopenharmony_ci * @udev: usb_device of a new device
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci * Allocates and initializes a new stub_device struct.
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_cistatic struct stub_device *stub_device_alloc(struct usb_device *udev)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct stub_device *sdev;
26062306a36Sopenharmony_ci	int busnum = udev->bus->busnum;
26162306a36Sopenharmony_ci	int devnum = udev->devnum;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	dev_dbg(&udev->dev, "allocating stub device");
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* yes, it's a new device */
26662306a36Sopenharmony_ci	sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
26762306a36Sopenharmony_ci	if (!sdev)
26862306a36Sopenharmony_ci		return NULL;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	sdev->udev = usb_get_dev(udev);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * devid is defined with devnum when this driver is first allocated.
27462306a36Sopenharmony_ci	 * devnum may change later if a device is reset. However, devid never
27562306a36Sopenharmony_ci	 * changes during a usbip connection.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	sdev->devid		= (busnum << 16) | devnum;
27862306a36Sopenharmony_ci	sdev->ud.side		= USBIP_STUB;
27962306a36Sopenharmony_ci	sdev->ud.status		= SDEV_ST_AVAILABLE;
28062306a36Sopenharmony_ci	spin_lock_init(&sdev->ud.lock);
28162306a36Sopenharmony_ci	mutex_init(&sdev->ud.sysfs_lock);
28262306a36Sopenharmony_ci	sdev->ud.tcp_socket	= NULL;
28362306a36Sopenharmony_ci	sdev->ud.sockfd		= -1;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	INIT_LIST_HEAD(&sdev->priv_init);
28662306a36Sopenharmony_ci	INIT_LIST_HEAD(&sdev->priv_tx);
28762306a36Sopenharmony_ci	INIT_LIST_HEAD(&sdev->priv_free);
28862306a36Sopenharmony_ci	INIT_LIST_HEAD(&sdev->unlink_free);
28962306a36Sopenharmony_ci	INIT_LIST_HEAD(&sdev->unlink_tx);
29062306a36Sopenharmony_ci	spin_lock_init(&sdev->priv_lock);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	init_waitqueue_head(&sdev->tx_waitq);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	sdev->ud.eh_ops.shutdown = stub_shutdown_connection;
29562306a36Sopenharmony_ci	sdev->ud.eh_ops.reset    = stub_device_reset;
29662306a36Sopenharmony_ci	sdev->ud.eh_ops.unusable = stub_device_unusable;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	usbip_start_eh(&sdev->ud);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	dev_dbg(&udev->dev, "register new device\n");
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return sdev;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void stub_device_free(struct stub_device *sdev)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	kfree(sdev);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int stub_probe(struct usb_device *udev)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct stub_device *sdev = NULL;
31362306a36Sopenharmony_ci	const char *udev_busid = dev_name(&udev->dev);
31462306a36Sopenharmony_ci	struct bus_id_priv *busid_priv;
31562306a36Sopenharmony_ci	int rc = 0;
31662306a36Sopenharmony_ci	char save_status;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	dev_dbg(&udev->dev, "Enter probe\n");
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Not sure if this is our device. Allocate here to avoid
32162306a36Sopenharmony_ci	 * calling alloc while holding busid_table lock.
32262306a36Sopenharmony_ci	 */
32362306a36Sopenharmony_ci	sdev = stub_device_alloc(udev);
32462306a36Sopenharmony_ci	if (!sdev)
32562306a36Sopenharmony_ci		return -ENOMEM;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* check we should claim or not by busid_table */
32862306a36Sopenharmony_ci	busid_priv = get_busid_priv(udev_busid);
32962306a36Sopenharmony_ci	if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) ||
33062306a36Sopenharmony_ci	    (busid_priv->status == STUB_BUSID_OTHER)) {
33162306a36Sopenharmony_ci		dev_info(&udev->dev,
33262306a36Sopenharmony_ci			"%s is not in match_busid table... skip!\n",
33362306a36Sopenharmony_ci			udev_busid);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		/*
33662306a36Sopenharmony_ci		 * Return value should be ENODEV or ENOXIO to continue trying
33762306a36Sopenharmony_ci		 * other matched drivers by the driver core.
33862306a36Sopenharmony_ci		 * See driver_probe_device() in driver/base/dd.c
33962306a36Sopenharmony_ci		 */
34062306a36Sopenharmony_ci		rc = -ENODEV;
34162306a36Sopenharmony_ci		if (!busid_priv)
34262306a36Sopenharmony_ci			goto sdev_free;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		goto call_put_busid_priv;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
34862306a36Sopenharmony_ci		dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
34962306a36Sopenharmony_ci			 udev_busid);
35062306a36Sopenharmony_ci		rc = -ENODEV;
35162306a36Sopenharmony_ci		goto call_put_busid_priv;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
35562306a36Sopenharmony_ci		dev_dbg(&udev->dev,
35662306a36Sopenharmony_ci			"%s is attached on vhci_hcd... skip!\n",
35762306a36Sopenharmony_ci			udev_busid);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		rc = -ENODEV;
36062306a36Sopenharmony_ci		goto call_put_busid_priv;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	dev_info(&udev->dev,
36562306a36Sopenharmony_ci		"usbip-host: register new device (bus %u dev %u)\n",
36662306a36Sopenharmony_ci		udev->bus->busnum, udev->devnum);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	busid_priv->shutdown_busid = 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* set private data to usb_device */
37162306a36Sopenharmony_ci	dev_set_drvdata(&udev->dev, sdev);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	busid_priv->sdev = sdev;
37462306a36Sopenharmony_ci	busid_priv->udev = udev;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	save_status = busid_priv->status;
37762306a36Sopenharmony_ci	busid_priv->status = STUB_BUSID_ALLOC;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* release the busid_lock */
38062306a36Sopenharmony_ci	put_busid_priv(busid_priv);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/*
38362306a36Sopenharmony_ci	 * Claim this hub port.
38462306a36Sopenharmony_ci	 * It doesn't matter what value we pass as owner
38562306a36Sopenharmony_ci	 * (struct dev_state) as long as it is unique.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	rc = usb_hub_claim_port(udev->parent, udev->portnum,
38862306a36Sopenharmony_ci			(struct usb_dev_state *) udev);
38962306a36Sopenharmony_ci	if (rc) {
39062306a36Sopenharmony_ci		dev_dbg(&udev->dev, "unable to claim port\n");
39162306a36Sopenharmony_ci		goto err_port;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return 0;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cierr_port:
39762306a36Sopenharmony_ci	dev_set_drvdata(&udev->dev, NULL);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* we already have busid_priv, just lock busid_lock */
40062306a36Sopenharmony_ci	spin_lock(&busid_priv->busid_lock);
40162306a36Sopenharmony_ci	busid_priv->sdev = NULL;
40262306a36Sopenharmony_ci	busid_priv->status = save_status;
40362306a36Sopenharmony_ci	spin_unlock(&busid_priv->busid_lock);
40462306a36Sopenharmony_ci	/* lock is released - go to free */
40562306a36Sopenharmony_ci	goto sdev_free;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cicall_put_busid_priv:
40862306a36Sopenharmony_ci	/* release the busid_lock */
40962306a36Sopenharmony_ci	put_busid_priv(busid_priv);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cisdev_free:
41262306a36Sopenharmony_ci	usb_put_dev(udev);
41362306a36Sopenharmony_ci	stub_device_free(sdev);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return rc;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void shutdown_busid(struct bus_id_priv *busid_priv)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* wait for the stop of the event handler */
42362306a36Sopenharmony_ci	usbip_stop_eh(&busid_priv->sdev->ud);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/*
42762306a36Sopenharmony_ci * called in usb_disconnect() or usb_deregister()
42862306a36Sopenharmony_ci * but only if actconfig(active configuration) exists
42962306a36Sopenharmony_ci */
43062306a36Sopenharmony_cistatic void stub_disconnect(struct usb_device *udev)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct stub_device *sdev;
43362306a36Sopenharmony_ci	const char *udev_busid = dev_name(&udev->dev);
43462306a36Sopenharmony_ci	struct bus_id_priv *busid_priv;
43562306a36Sopenharmony_ci	int rc;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	dev_dbg(&udev->dev, "Enter disconnect\n");
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	busid_priv = get_busid_priv(udev_busid);
44062306a36Sopenharmony_ci	if (!busid_priv) {
44162306a36Sopenharmony_ci		BUG();
44262306a36Sopenharmony_ci		return;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	sdev = dev_get_drvdata(&udev->dev);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* get stub_device */
44862306a36Sopenharmony_ci	if (!sdev) {
44962306a36Sopenharmony_ci		dev_err(&udev->dev, "could not get device");
45062306a36Sopenharmony_ci		/* release busid_lock */
45162306a36Sopenharmony_ci		put_busid_priv(busid_priv);
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	dev_set_drvdata(&udev->dev, NULL);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* release busid_lock before call to remove device files */
45862306a36Sopenharmony_ci	put_busid_priv(busid_priv);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/*
46162306a36Sopenharmony_ci	 * NOTE: rx/tx threads are invoked for each usb_device.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* release port */
46562306a36Sopenharmony_ci	rc = usb_hub_release_port(udev->parent, udev->portnum,
46662306a36Sopenharmony_ci				  (struct usb_dev_state *) udev);
46762306a36Sopenharmony_ci	/*
46862306a36Sopenharmony_ci	 * NOTE: If a HUB disconnect triggered disconnect of the down stream
46962306a36Sopenharmony_ci	 * device usb_hub_release_port will return -ENODEV so we can safely ignore
47062306a36Sopenharmony_ci	 * that error here.
47162306a36Sopenharmony_ci	 */
47262306a36Sopenharmony_ci	if (rc && (rc != -ENODEV)) {
47362306a36Sopenharmony_ci		dev_dbg(&udev->dev, "unable to release port (%i)\n", rc);
47462306a36Sopenharmony_ci		return;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* If usb reset is called from event handler */
47862306a36Sopenharmony_ci	if (usbip_in_eh(current))
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* we already have busid_priv, just lock busid_lock */
48262306a36Sopenharmony_ci	spin_lock(&busid_priv->busid_lock);
48362306a36Sopenharmony_ci	if (!busid_priv->shutdown_busid)
48462306a36Sopenharmony_ci		busid_priv->shutdown_busid = 1;
48562306a36Sopenharmony_ci	/* release busid_lock */
48662306a36Sopenharmony_ci	spin_unlock(&busid_priv->busid_lock);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* shutdown the current connection */
48962306a36Sopenharmony_ci	shutdown_busid(busid_priv);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	usb_put_dev(sdev->udev);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* we already have busid_priv, just lock busid_lock */
49462306a36Sopenharmony_ci	spin_lock(&busid_priv->busid_lock);
49562306a36Sopenharmony_ci	/* free sdev */
49662306a36Sopenharmony_ci	busid_priv->sdev = NULL;
49762306a36Sopenharmony_ci	stub_device_free(sdev);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (busid_priv->status == STUB_BUSID_ALLOC)
50062306a36Sopenharmony_ci		busid_priv->status = STUB_BUSID_ADDED;
50162306a36Sopenharmony_ci	/* release busid_lock */
50262306a36Sopenharmony_ci	spin_unlock(&busid_priv->busid_lock);
50362306a36Sopenharmony_ci	return;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci#ifdef CONFIG_PM
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci/* These functions need usb_port_suspend and usb_port_resume,
50962306a36Sopenharmony_ci * which reside in drivers/usb/core/usb.h. Skip for now. */
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int stub_suspend(struct usb_device *udev, pm_message_t message)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	dev_dbg(&udev->dev, "stub_suspend\n");
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int stub_resume(struct usb_device *udev, pm_message_t message)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	dev_dbg(&udev->dev, "stub_resume\n");
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return 0;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci#endif	/* CONFIG_PM */
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistruct usb_device_driver stub_driver = {
52862306a36Sopenharmony_ci	.name		= "usbip-host",
52962306a36Sopenharmony_ci	.probe		= stub_probe,
53062306a36Sopenharmony_ci	.disconnect	= stub_disconnect,
53162306a36Sopenharmony_ci#ifdef CONFIG_PM
53262306a36Sopenharmony_ci	.suspend	= stub_suspend,
53362306a36Sopenharmony_ci	.resume		= stub_resume,
53462306a36Sopenharmony_ci#endif
53562306a36Sopenharmony_ci	.supports_autosuspend	=	0,
53662306a36Sopenharmony_ci	.dev_groups	= usbip_groups,
53762306a36Sopenharmony_ci};
538