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