18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2003-2008 Takahiro Hirofuchi 48c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Nobuo Iwata 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/file.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/kthread.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "usbip_common.h" 168c2ecf20Sopenharmony_ci#include "vhci.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Takahiro Hirofuchi" 198c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * TODO 238c2ecf20Sopenharmony_ci * - update root hub emulation 248c2ecf20Sopenharmony_ci * - move the emulation code to userland ? 258c2ecf20Sopenharmony_ci * porting to other operating systems 268c2ecf20Sopenharmony_ci * minimize kernel code 278c2ecf20Sopenharmony_ci * - add suspend/resume code 288c2ecf20Sopenharmony_ci * - clean up everything 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* See usb gadget dummy hcd */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int vhci_hub_status(struct usb_hcd *hcd, char *buff); 348c2ecf20Sopenharmony_cistatic int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 358c2ecf20Sopenharmony_ci u16 wIndex, char *buff, u16 wLength); 368c2ecf20Sopenharmony_cistatic int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, 378c2ecf20Sopenharmony_ci gfp_t mem_flags); 388c2ecf20Sopenharmony_cistatic int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); 398c2ecf20Sopenharmony_cistatic int vhci_start(struct usb_hcd *vhci_hcd); 408c2ecf20Sopenharmony_cistatic void vhci_stop(struct usb_hcd *hcd); 418c2ecf20Sopenharmony_cistatic int vhci_get_frame_number(struct usb_hcd *hcd); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const char driver_name[] = "vhci_hcd"; 448c2ecf20Sopenharmony_cistatic const char driver_desc[] = "USB/IP Virtual Host Controller"; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciint vhci_num_controllers = VHCI_NR_HCS; 478c2ecf20Sopenharmony_cistruct vhci *vhcis; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const char * const bit_desc[] = { 508c2ecf20Sopenharmony_ci "CONNECTION", /*0*/ 518c2ecf20Sopenharmony_ci "ENABLE", /*1*/ 528c2ecf20Sopenharmony_ci "SUSPEND", /*2*/ 538c2ecf20Sopenharmony_ci "OVER_CURRENT", /*3*/ 548c2ecf20Sopenharmony_ci "RESET", /*4*/ 558c2ecf20Sopenharmony_ci "L1", /*5*/ 568c2ecf20Sopenharmony_ci "R6", /*6*/ 578c2ecf20Sopenharmony_ci "R7", /*7*/ 588c2ecf20Sopenharmony_ci "POWER", /*8*/ 598c2ecf20Sopenharmony_ci "LOWSPEED", /*9*/ 608c2ecf20Sopenharmony_ci "HIGHSPEED", /*10*/ 618c2ecf20Sopenharmony_ci "PORT_TEST", /*11*/ 628c2ecf20Sopenharmony_ci "INDICATOR", /*12*/ 638c2ecf20Sopenharmony_ci "R13", /*13*/ 648c2ecf20Sopenharmony_ci "R14", /*14*/ 658c2ecf20Sopenharmony_ci "R15", /*15*/ 668c2ecf20Sopenharmony_ci "C_CONNECTION", /*16*/ 678c2ecf20Sopenharmony_ci "C_ENABLE", /*17*/ 688c2ecf20Sopenharmony_ci "C_SUSPEND", /*18*/ 698c2ecf20Sopenharmony_ci "C_OVER_CURRENT", /*19*/ 708c2ecf20Sopenharmony_ci "C_RESET", /*20*/ 718c2ecf20Sopenharmony_ci "C_L1", /*21*/ 728c2ecf20Sopenharmony_ci "R22", /*22*/ 738c2ecf20Sopenharmony_ci "R23", /*23*/ 748c2ecf20Sopenharmony_ci "R24", /*24*/ 758c2ecf20Sopenharmony_ci "R25", /*25*/ 768c2ecf20Sopenharmony_ci "R26", /*26*/ 778c2ecf20Sopenharmony_ci "R27", /*27*/ 788c2ecf20Sopenharmony_ci "R28", /*28*/ 798c2ecf20Sopenharmony_ci "R29", /*29*/ 808c2ecf20Sopenharmony_ci "R30", /*30*/ 818c2ecf20Sopenharmony_ci "R31", /*31*/ 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic const char * const bit_desc_ss[] = { 858c2ecf20Sopenharmony_ci "CONNECTION", /*0*/ 868c2ecf20Sopenharmony_ci "ENABLE", /*1*/ 878c2ecf20Sopenharmony_ci "SUSPEND", /*2*/ 888c2ecf20Sopenharmony_ci "OVER_CURRENT", /*3*/ 898c2ecf20Sopenharmony_ci "RESET", /*4*/ 908c2ecf20Sopenharmony_ci "L1", /*5*/ 918c2ecf20Sopenharmony_ci "R6", /*6*/ 928c2ecf20Sopenharmony_ci "R7", /*7*/ 938c2ecf20Sopenharmony_ci "R8", /*8*/ 948c2ecf20Sopenharmony_ci "POWER", /*9*/ 958c2ecf20Sopenharmony_ci "HIGHSPEED", /*10*/ 968c2ecf20Sopenharmony_ci "PORT_TEST", /*11*/ 978c2ecf20Sopenharmony_ci "INDICATOR", /*12*/ 988c2ecf20Sopenharmony_ci "R13", /*13*/ 998c2ecf20Sopenharmony_ci "R14", /*14*/ 1008c2ecf20Sopenharmony_ci "R15", /*15*/ 1018c2ecf20Sopenharmony_ci "C_CONNECTION", /*16*/ 1028c2ecf20Sopenharmony_ci "C_ENABLE", /*17*/ 1038c2ecf20Sopenharmony_ci "C_SUSPEND", /*18*/ 1048c2ecf20Sopenharmony_ci "C_OVER_CURRENT", /*19*/ 1058c2ecf20Sopenharmony_ci "C_RESET", /*20*/ 1068c2ecf20Sopenharmony_ci "C_BH_RESET", /*21*/ 1078c2ecf20Sopenharmony_ci "C_LINK_STATE", /*22*/ 1088c2ecf20Sopenharmony_ci "C_CONFIG_ERROR", /*23*/ 1098c2ecf20Sopenharmony_ci "R24", /*24*/ 1108c2ecf20Sopenharmony_ci "R25", /*25*/ 1118c2ecf20Sopenharmony_ci "R26", /*26*/ 1128c2ecf20Sopenharmony_ci "R27", /*27*/ 1138c2ecf20Sopenharmony_ci "R28", /*28*/ 1148c2ecf20Sopenharmony_ci "R29", /*29*/ 1158c2ecf20Sopenharmony_ci "R30", /*30*/ 1168c2ecf20Sopenharmony_ci "R31", /*31*/ 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void dump_port_status_diff(u32 prev_status, u32 new_status, bool usb3) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci int i = 0; 1228c2ecf20Sopenharmony_ci u32 bit = 1; 1238c2ecf20Sopenharmony_ci const char * const *desc = bit_desc; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (usb3) 1268c2ecf20Sopenharmony_ci desc = bit_desc_ss; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status); 1298c2ecf20Sopenharmony_ci while (bit) { 1308c2ecf20Sopenharmony_ci u32 prev = prev_status & bit; 1318c2ecf20Sopenharmony_ci u32 new = new_status & bit; 1328c2ecf20Sopenharmony_ci char change; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!prev && new) 1358c2ecf20Sopenharmony_ci change = '+'; 1368c2ecf20Sopenharmony_ci else if (prev && !new) 1378c2ecf20Sopenharmony_ci change = '-'; 1388c2ecf20Sopenharmony_ci else 1398c2ecf20Sopenharmony_ci change = ' '; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (prev || new) { 1428c2ecf20Sopenharmony_ci pr_debug(" %c%s\n", change, desc[i]); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (bit == 1) /* USB_PORT_STAT_CONNECTION */ 1458c2ecf20Sopenharmony_ci pr_debug(" %c%s\n", change, "USB_PORT_STAT_SPEED_5GBPS"); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci bit <<= 1; 1488c2ecf20Sopenharmony_ci i++; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci pr_debug("\n"); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_civoid rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); 1568c2ecf20Sopenharmony_ci struct vhci *vhci = vhci_hcd->vhci; 1578c2ecf20Sopenharmony_ci int rhport = vdev->rhport; 1588c2ecf20Sopenharmony_ci u32 status; 1598c2ecf20Sopenharmony_ci unsigned long flags; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci status = vhci_hcd->port_status[rhport]; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci switch (speed) { 1708c2ecf20Sopenharmony_ci case USB_SPEED_HIGH: 1718c2ecf20Sopenharmony_ci status |= USB_PORT_STAT_HIGH_SPEED; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case USB_SPEED_LOW: 1748c2ecf20Sopenharmony_ci status |= USB_PORT_STAT_LOW_SPEED; 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci default: 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] = status; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci usb_hcd_poll_rh_status(vhci_hcd_to_hcd(vhci_hcd)); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void rh_port_disconnect(struct vhci_device *vdev) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); 1908c2ecf20Sopenharmony_ci struct vhci *vhci = vhci_hcd->vhci; 1918c2ecf20Sopenharmony_ci int rhport = vdev->rhport; 1928c2ecf20Sopenharmony_ci u32 status; 1938c2ecf20Sopenharmony_ci unsigned long flags; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci status = vhci_hcd->port_status[rhport]; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci status &= ~USB_PORT_STAT_CONNECTION; 2028c2ecf20Sopenharmony_ci status |= (1 << USB_PORT_FEAT_C_CONNECTION); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] = status; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 2078c2ecf20Sopenharmony_ci usb_hcd_poll_rh_status(vhci_hcd_to_hcd(vhci_hcd)); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define PORT_C_MASK \ 2118c2ecf20Sopenharmony_ci ((USB_PORT_STAT_C_CONNECTION \ 2128c2ecf20Sopenharmony_ci | USB_PORT_STAT_C_ENABLE \ 2138c2ecf20Sopenharmony_ci | USB_PORT_STAT_C_SUSPEND \ 2148c2ecf20Sopenharmony_ci | USB_PORT_STAT_C_OVERCURRENT \ 2158c2ecf20Sopenharmony_ci | USB_PORT_STAT_C_RESET) << 16) 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * Returns 0 if the status hasn't changed, or the number of bytes in buf. 2198c2ecf20Sopenharmony_ci * Ports are 0-indexed from the HCD point of view, 2208c2ecf20Sopenharmony_ci * and 1-indexed from the USB core pointer of view. 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * @buf: a bitmap to show which port status has been changed. 2238c2ecf20Sopenharmony_ci * bit 0: reserved 2248c2ecf20Sopenharmony_ci * bit 1: the status of port 0 has been changed. 2258c2ecf20Sopenharmony_ci * bit 2: the status of port 1 has been changed. 2268c2ecf20Sopenharmony_ci * ... 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_cistatic int vhci_hub_status(struct usb_hcd *hcd, char *buf) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); 2318c2ecf20Sopenharmony_ci struct vhci *vhci = vhci_hcd->vhci; 2328c2ecf20Sopenharmony_ci int retval = DIV_ROUND_UP(VHCI_HC_PORTS + 1, 8); 2338c2ecf20Sopenharmony_ci int rhport; 2348c2ecf20Sopenharmony_ci int changed = 0; 2358c2ecf20Sopenharmony_ci unsigned long flags; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci memset(buf, 0, retval); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 2408c2ecf20Sopenharmony_ci if (!HCD_HW_ACCESSIBLE(hcd)) { 2418c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh("hw accessible flag not on?\n"); 2428c2ecf20Sopenharmony_ci goto done; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* check pseudo status register for each port */ 2468c2ecf20Sopenharmony_ci for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { 2478c2ecf20Sopenharmony_ci if ((vhci_hcd->port_status[rhport] & PORT_C_MASK)) { 2488c2ecf20Sopenharmony_ci /* The status of a port has been changed, */ 2498c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh("port %d status changed\n", rhport); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8; 2528c2ecf20Sopenharmony_ci changed = 1; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) 2578c2ecf20Sopenharmony_ci usb_hcd_resume_root_hub(hcd); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cidone: 2608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 2618c2ecf20Sopenharmony_ci return changed ? retval : 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* usb 3.0 root hub device descriptor */ 2658c2ecf20Sopenharmony_cistatic struct { 2668c2ecf20Sopenharmony_ci struct usb_bos_descriptor bos; 2678c2ecf20Sopenharmony_ci struct usb_ss_cap_descriptor ss_cap; 2688c2ecf20Sopenharmony_ci} __packed usb3_bos_desc = { 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci .bos = { 2718c2ecf20Sopenharmony_ci .bLength = USB_DT_BOS_SIZE, 2728c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_BOS, 2738c2ecf20Sopenharmony_ci .wTotalLength = cpu_to_le16(sizeof(usb3_bos_desc)), 2748c2ecf20Sopenharmony_ci .bNumDeviceCaps = 1, 2758c2ecf20Sopenharmony_ci }, 2768c2ecf20Sopenharmony_ci .ss_cap = { 2778c2ecf20Sopenharmony_ci .bLength = USB_DT_USB_SS_CAP_SIZE, 2788c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE_CAPABILITY, 2798c2ecf20Sopenharmony_ci .bDevCapabilityType = USB_SS_CAP_TYPE, 2808c2ecf20Sopenharmony_ci .wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION), 2818c2ecf20Sopenharmony_ci .bFunctionalitySupport = ilog2(USB_5GBPS_OPERATION), 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic inline void 2868c2ecf20Sopenharmony_ciss_hub_descriptor(struct usb_hub_descriptor *desc) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci memset(desc, 0, sizeof *desc); 2898c2ecf20Sopenharmony_ci desc->bDescriptorType = USB_DT_SS_HUB; 2908c2ecf20Sopenharmony_ci desc->bDescLength = 12; 2918c2ecf20Sopenharmony_ci desc->wHubCharacteristics = cpu_to_le16( 2928c2ecf20Sopenharmony_ci HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM); 2938c2ecf20Sopenharmony_ci desc->bNbrPorts = VHCI_HC_PORTS; 2948c2ecf20Sopenharmony_ci desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/ 2958c2ecf20Sopenharmony_ci desc->u.ss.DeviceRemovable = 0xffff; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic inline void hub_descriptor(struct usb_hub_descriptor *desc) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci int width; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 3038c2ecf20Sopenharmony_ci desc->bDescriptorType = USB_DT_HUB; 3048c2ecf20Sopenharmony_ci desc->wHubCharacteristics = cpu_to_le16( 3058c2ecf20Sopenharmony_ci HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci desc->bNbrPorts = VHCI_HC_PORTS; 3088c2ecf20Sopenharmony_ci BUILD_BUG_ON(VHCI_HC_PORTS > USB_MAXCHILDREN); 3098c2ecf20Sopenharmony_ci width = desc->bNbrPorts / 8 + 1; 3108c2ecf20Sopenharmony_ci desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * width; 3118c2ecf20Sopenharmony_ci memset(&desc->u.hs.DeviceRemovable[0], 0, width); 3128c2ecf20Sopenharmony_ci memset(&desc->u.hs.DeviceRemovable[width], 0xff, width); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 3168c2ecf20Sopenharmony_ci u16 wIndex, char *buf, u16 wLength) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd; 3198c2ecf20Sopenharmony_ci struct vhci *vhci; 3208c2ecf20Sopenharmony_ci int retval = 0; 3218c2ecf20Sopenharmony_ci int rhport = -1; 3228c2ecf20Sopenharmony_ci unsigned long flags; 3238c2ecf20Sopenharmony_ci bool invalid_rhport = false; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci u32 prev_port_status[VHCI_HC_PORTS]; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!HCD_HW_ACCESSIBLE(hcd)) 3288c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * NOTE: 3328c2ecf20Sopenharmony_ci * wIndex (bits 0-7) shows the port number and begins from 1? 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci wIndex = ((__u8)(wIndex & 0x00ff)); 3358c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, 3368c2ecf20Sopenharmony_ci wIndex); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * wIndex can be 0 for some request types (typeReq). rhport is 3408c2ecf20Sopenharmony_ci * in valid range when wIndex >= 1 and < VHCI_HC_PORTS. 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * Reference port_status[] only with valid rhport when 3438c2ecf20Sopenharmony_ci * invalid_rhport is false. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci if (wIndex < 1 || wIndex > VHCI_HC_PORTS) { 3468c2ecf20Sopenharmony_ci invalid_rhport = true; 3478c2ecf20Sopenharmony_ci if (wIndex > VHCI_HC_PORTS) 3488c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 3498c2ecf20Sopenharmony_ci } else 3508c2ecf20Sopenharmony_ci rhport = wIndex - 1; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci vhci_hcd = hcd_to_vhci_hcd(hcd); 3538c2ecf20Sopenharmony_ci vhci = vhci_hcd->vhci; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* store old status and compare now and old later */ 3588c2ecf20Sopenharmony_ci if (usbip_dbg_flag_vhci_rh) { 3598c2ecf20Sopenharmony_ci if (!invalid_rhport) 3608c2ecf20Sopenharmony_ci memcpy(prev_port_status, vhci_hcd->port_status, 3618c2ecf20Sopenharmony_ci sizeof(prev_port_status)); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci switch (typeReq) { 3658c2ecf20Sopenharmony_ci case ClearHubFeature: 3668c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" ClearHubFeature\n"); 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case ClearPortFeature: 3698c2ecf20Sopenharmony_ci if (invalid_rhport) { 3708c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 3718c2ecf20Sopenharmony_ci goto error; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci switch (wValue) { 3748c2ecf20Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 3758c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) { 3768c2ecf20Sopenharmony_ci pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not " 3778c2ecf20Sopenharmony_ci "supported for USB 3.0 roothub\n"); 3788c2ecf20Sopenharmony_ci goto error; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 3818c2ecf20Sopenharmony_ci " ClearPortFeature: USB_PORT_FEAT_SUSPEND\n"); 3828c2ecf20Sopenharmony_ci if (vhci_hcd->port_status[rhport] & USB_PORT_STAT_SUSPEND) { 3838c2ecf20Sopenharmony_ci /* 20msec signaling */ 3848c2ecf20Sopenharmony_ci vhci_hcd->resuming = 1; 3858c2ecf20Sopenharmony_ci vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(20); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci case USB_PORT_FEAT_POWER: 3898c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 3908c2ecf20Sopenharmony_ci " ClearPortFeature: USB_PORT_FEAT_POWER\n"); 3918c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) 3928c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] &= ~USB_SS_PORT_STAT_POWER; 3938c2ecf20Sopenharmony_ci else 3948c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] &= ~USB_PORT_STAT_POWER; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci default: 3978c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n", 3988c2ecf20Sopenharmony_ci wValue); 3998c2ecf20Sopenharmony_ci if (wValue >= 32) 4008c2ecf20Sopenharmony_ci goto error; 4018c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] &= ~(1 << wValue); 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci case GetHubDescriptor: 4068c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" GetHubDescriptor\n"); 4078c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3 && 4088c2ecf20Sopenharmony_ci (wLength < USB_DT_SS_HUB_SIZE || 4098c2ecf20Sopenharmony_ci wValue != (USB_DT_SS_HUB << 8))) { 4108c2ecf20Sopenharmony_ci pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n"); 4118c2ecf20Sopenharmony_ci goto error; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) 4148c2ecf20Sopenharmony_ci ss_hub_descriptor((struct usb_hub_descriptor *) buf); 4158c2ecf20Sopenharmony_ci else 4168c2ecf20Sopenharmony_ci hub_descriptor((struct usb_hub_descriptor *) buf); 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci case DeviceRequest | USB_REQ_GET_DESCRIPTOR: 4198c2ecf20Sopenharmony_ci if (hcd->speed != HCD_USB3) 4208c2ecf20Sopenharmony_ci goto error; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if ((wValue >> 8) != USB_DT_BOS) 4238c2ecf20Sopenharmony_ci goto error; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc)); 4268c2ecf20Sopenharmony_ci retval = sizeof(usb3_bos_desc); 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case GetHubStatus: 4298c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" GetHubStatus\n"); 4308c2ecf20Sopenharmony_ci *(__le32 *) buf = cpu_to_le32(0); 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci case GetPortStatus: 4338c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); 4348c2ecf20Sopenharmony_ci if (invalid_rhport) { 4358c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 4368c2ecf20Sopenharmony_ci retval = -EPIPE; 4378c2ecf20Sopenharmony_ci goto error; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* we do not care about resume. */ 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* whoever resets or resumes must GetPortStatus to 4438c2ecf20Sopenharmony_ci * complete it!! 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci if (vhci_hcd->resuming && time_after(jiffies, vhci_hcd->re_timeout)) { 4468c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= (1 << USB_PORT_FEAT_C_SUSPEND); 4478c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] &= ~(1 << USB_PORT_FEAT_SUSPEND); 4488c2ecf20Sopenharmony_ci vhci_hcd->resuming = 0; 4498c2ecf20Sopenharmony_ci vhci_hcd->re_timeout = 0; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if ((vhci_hcd->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != 4538c2ecf20Sopenharmony_ci 0 && time_after(jiffies, vhci_hcd->re_timeout)) { 4548c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= (1 << USB_PORT_FEAT_C_RESET); 4558c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] &= ~(1 << USB_PORT_FEAT_RESET); 4568c2ecf20Sopenharmony_ci vhci_hcd->re_timeout = 0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* 4598c2ecf20Sopenharmony_ci * A few drivers do usb reset during probe when 4608c2ecf20Sopenharmony_ci * the device could be in VDEV_ST_USED state 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci if (vhci_hcd->vdev[rhport].ud.status == 4638c2ecf20Sopenharmony_ci VDEV_ST_NOTASSIGNED || 4648c2ecf20Sopenharmony_ci vhci_hcd->vdev[rhport].ud.status == 4658c2ecf20Sopenharmony_ci VDEV_ST_USED) { 4668c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 4678c2ecf20Sopenharmony_ci " enable rhport %d (status %u)\n", 4688c2ecf20Sopenharmony_ci rhport, 4698c2ecf20Sopenharmony_ci vhci_hcd->vdev[rhport].ud.status); 4708c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= 4718c2ecf20Sopenharmony_ci USB_PORT_STAT_ENABLE; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (hcd->speed < HCD_USB3) { 4758c2ecf20Sopenharmony_ci switch (vhci_hcd->vdev[rhport].speed) { 4768c2ecf20Sopenharmony_ci case USB_SPEED_HIGH: 4778c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= 4788c2ecf20Sopenharmony_ci USB_PORT_STAT_HIGH_SPEED; 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci case USB_SPEED_LOW: 4818c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= 4828c2ecf20Sopenharmony_ci USB_PORT_STAT_LOW_SPEED; 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci default: 4858c2ecf20Sopenharmony_ci pr_err("vhci_device speed not set\n"); 4868c2ecf20Sopenharmony_ci break; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci ((__le16 *) buf)[0] = cpu_to_le16(vhci_hcd->port_status[rhport]); 4918c2ecf20Sopenharmony_ci ((__le16 *) buf)[1] = 4928c2ecf20Sopenharmony_ci cpu_to_le16(vhci_hcd->port_status[rhport] >> 16); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0], 4958c2ecf20Sopenharmony_ci ((u16 *)buf)[1]); 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci case SetHubFeature: 4988c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" SetHubFeature\n"); 4998c2ecf20Sopenharmony_ci retval = -EPIPE; 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci case SetPortFeature: 5028c2ecf20Sopenharmony_ci switch (wValue) { 5038c2ecf20Sopenharmony_ci case USB_PORT_FEAT_LINK_STATE: 5048c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5058c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_LINK_STATE\n"); 5068c2ecf20Sopenharmony_ci if (hcd->speed != HCD_USB3) { 5078c2ecf20Sopenharmony_ci pr_err("USB_PORT_FEAT_LINK_STATE req not " 5088c2ecf20Sopenharmony_ci "supported for USB 2.0 roothub\n"); 5098c2ecf20Sopenharmony_ci goto error; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * Since this is dummy we don't have an actual link so 5138c2ecf20Sopenharmony_ci * there is nothing to do for the SET_LINK_STATE cmd 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci case USB_PORT_FEAT_U1_TIMEOUT: 5178c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5188c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_U1_TIMEOUT\n"); 5198c2ecf20Sopenharmony_ci fallthrough; 5208c2ecf20Sopenharmony_ci case USB_PORT_FEAT_U2_TIMEOUT: 5218c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5228c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); 5238c2ecf20Sopenharmony_ci /* TODO: add suspend/resume support! */ 5248c2ecf20Sopenharmony_ci if (hcd->speed != HCD_USB3) { 5258c2ecf20Sopenharmony_ci pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not " 5268c2ecf20Sopenharmony_ci "supported for USB 2.0 roothub\n"); 5278c2ecf20Sopenharmony_ci goto error; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 5318c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5328c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); 5338c2ecf20Sopenharmony_ci /* Applicable only for USB2.0 hub */ 5348c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) { 5358c2ecf20Sopenharmony_ci pr_err("USB_PORT_FEAT_SUSPEND req not " 5368c2ecf20Sopenharmony_ci "supported for USB 3.0 roothub\n"); 5378c2ecf20Sopenharmony_ci goto error; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (invalid_rhport) { 5418c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 5428c2ecf20Sopenharmony_ci goto error; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND; 5468c2ecf20Sopenharmony_ci break; 5478c2ecf20Sopenharmony_ci case USB_PORT_FEAT_POWER: 5488c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5498c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_POWER\n"); 5508c2ecf20Sopenharmony_ci if (invalid_rhport) { 5518c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 5528c2ecf20Sopenharmony_ci goto error; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) 5558c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER; 5568c2ecf20Sopenharmony_ci else 5578c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= USB_PORT_STAT_POWER; 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci case USB_PORT_FEAT_BH_PORT_RESET: 5608c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5618c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); 5628c2ecf20Sopenharmony_ci if (invalid_rhport) { 5638c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 5648c2ecf20Sopenharmony_ci goto error; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci /* Applicable only for USB3.0 hub */ 5678c2ecf20Sopenharmony_ci if (hcd->speed != HCD_USB3) { 5688c2ecf20Sopenharmony_ci pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " 5698c2ecf20Sopenharmony_ci "supported for USB 2.0 roothub\n"); 5708c2ecf20Sopenharmony_ci goto error; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci fallthrough; 5738c2ecf20Sopenharmony_ci case USB_PORT_FEAT_RESET: 5748c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh( 5758c2ecf20Sopenharmony_ci " SetPortFeature: USB_PORT_FEAT_RESET\n"); 5768c2ecf20Sopenharmony_ci if (invalid_rhport) { 5778c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 5788c2ecf20Sopenharmony_ci goto error; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci /* if it's already enabled, disable */ 5818c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) { 5828c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] = 0; 5838c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] = 5848c2ecf20Sopenharmony_ci (USB_SS_PORT_STAT_POWER | 5858c2ecf20Sopenharmony_ci USB_PORT_STAT_CONNECTION | 5868c2ecf20Sopenharmony_ci USB_PORT_STAT_RESET); 5878c2ecf20Sopenharmony_ci } else if (vhci_hcd->port_status[rhport] & USB_PORT_STAT_ENABLE) { 5888c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] &= ~(USB_PORT_STAT_ENABLE 5898c2ecf20Sopenharmony_ci | USB_PORT_STAT_LOW_SPEED 5908c2ecf20Sopenharmony_ci | USB_PORT_STAT_HIGH_SPEED); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* 50msec reset signaling */ 5948c2ecf20Sopenharmony_ci vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(50); 5958c2ecf20Sopenharmony_ci fallthrough; 5968c2ecf20Sopenharmony_ci default: 5978c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", 5988c2ecf20Sopenharmony_ci wValue); 5998c2ecf20Sopenharmony_ci if (invalid_rhport) { 6008c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", wIndex); 6018c2ecf20Sopenharmony_ci goto error; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci if (wValue >= 32) 6048c2ecf20Sopenharmony_ci goto error; 6058c2ecf20Sopenharmony_ci if (hcd->speed == HCD_USB3) { 6068c2ecf20Sopenharmony_ci if ((vhci_hcd->port_status[rhport] & 6078c2ecf20Sopenharmony_ci USB_SS_PORT_STAT_POWER) != 0) { 6088c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= (1 << wValue); 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci } else 6118c2ecf20Sopenharmony_ci if ((vhci_hcd->port_status[rhport] & 6128c2ecf20Sopenharmony_ci USB_PORT_STAT_POWER) != 0) { 6138c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport] |= (1 << wValue); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case GetPortErrorCount: 6188c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" GetPortErrorCount\n"); 6198c2ecf20Sopenharmony_ci if (hcd->speed != HCD_USB3) { 6208c2ecf20Sopenharmony_ci pr_err("GetPortErrorCount req not " 6218c2ecf20Sopenharmony_ci "supported for USB 2.0 roothub\n"); 6228c2ecf20Sopenharmony_ci goto error; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci /* We'll always return 0 since this is a dummy hub */ 6258c2ecf20Sopenharmony_ci *(__le32 *) buf = cpu_to_le32(0); 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci case SetHubDepth: 6288c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" SetHubDepth\n"); 6298c2ecf20Sopenharmony_ci if (hcd->speed != HCD_USB3) { 6308c2ecf20Sopenharmony_ci pr_err("SetHubDepth req not supported for " 6318c2ecf20Sopenharmony_ci "USB 2.0 roothub\n"); 6328c2ecf20Sopenharmony_ci goto error; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci default: 6368c2ecf20Sopenharmony_ci pr_err("default hub control req: %04x v%04x i%04x l%d\n", 6378c2ecf20Sopenharmony_ci typeReq, wValue, wIndex, wLength); 6388c2ecf20Sopenharmony_cierror: 6398c2ecf20Sopenharmony_ci /* "protocol stall" on error */ 6408c2ecf20Sopenharmony_ci retval = -EPIPE; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (usbip_dbg_flag_vhci_rh) { 6448c2ecf20Sopenharmony_ci pr_debug("port %d\n", rhport); 6458c2ecf20Sopenharmony_ci /* Only dump valid port status */ 6468c2ecf20Sopenharmony_ci if (!invalid_rhport) { 6478c2ecf20Sopenharmony_ci dump_port_status_diff(prev_port_status[rhport], 6488c2ecf20Sopenharmony_ci vhci_hcd->port_status[rhport], 6498c2ecf20Sopenharmony_ci hcd->speed == HCD_USB3); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci usbip_dbg_vhci_rh(" bye\n"); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (!invalid_rhport && 6578c2ecf20Sopenharmony_ci (vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0) { 6588c2ecf20Sopenharmony_ci usb_hcd_poll_rh_status(hcd); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return retval; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct vhci_priv *priv; 6678c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); 6688c2ecf20Sopenharmony_ci unsigned long flags; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); 6718c2ecf20Sopenharmony_ci if (!priv) { 6728c2ecf20Sopenharmony_ci usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); 6738c2ecf20Sopenharmony_ci return; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci spin_lock_irqsave(&vdev->priv_lock, flags); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci priv->seqnum = atomic_inc_return(&vhci_hcd->seqnum); 6798c2ecf20Sopenharmony_ci if (priv->seqnum == 0xffff) 6808c2ecf20Sopenharmony_ci dev_info(&urb->dev->dev, "seqnum max\n"); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci priv->vdev = vdev; 6838c2ecf20Sopenharmony_ci priv->urb = urb; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci urb->hcpriv = (void *) priv; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci list_add_tail(&priv->list, &vdev->priv_tx); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci wake_up(&vdev->waitq_tx); 6908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdev->priv_lock, flags); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); 6968c2ecf20Sopenharmony_ci struct vhci *vhci = vhci_hcd->vhci; 6978c2ecf20Sopenharmony_ci struct device *dev = &urb->dev->dev; 6988c2ecf20Sopenharmony_ci u8 portnum = urb->dev->portnum; 6998c2ecf20Sopenharmony_ci int ret = 0; 7008c2ecf20Sopenharmony_ci struct vhci_device *vdev; 7018c2ecf20Sopenharmony_ci unsigned long flags; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (portnum > VHCI_HC_PORTS) { 7048c2ecf20Sopenharmony_ci pr_err("invalid port number %d\n", portnum); 7058c2ecf20Sopenharmony_ci return -ENODEV; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci vdev = &vhci_hcd->vdev[portnum-1]; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (!urb->transfer_buffer && !urb->num_sgs && 7108c2ecf20Sopenharmony_ci urb->transfer_buffer_length) { 7118c2ecf20Sopenharmony_ci dev_dbg(dev, "Null URB transfer buffer\n"); 7128c2ecf20Sopenharmony_ci return -EINVAL; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (urb->status != -EINPROGRESS) { 7188c2ecf20Sopenharmony_ci dev_err(dev, "URB already unlinked!, status %d\n", urb->status); 7198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 7208c2ecf20Sopenharmony_ci return urb->status; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* refuse enqueue for dead connection */ 7248c2ecf20Sopenharmony_ci spin_lock(&vdev->ud.lock); 7258c2ecf20Sopenharmony_ci if (vdev->ud.status == VDEV_ST_NULL || 7268c2ecf20Sopenharmony_ci vdev->ud.status == VDEV_ST_ERROR) { 7278c2ecf20Sopenharmony_ci dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); 7288c2ecf20Sopenharmony_ci spin_unlock(&vdev->ud.lock); 7298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 7308c2ecf20Sopenharmony_ci return -ENODEV; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci spin_unlock(&vdev->ud.lock); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = usb_hcd_link_urb_to_ep(hcd, urb); 7358c2ecf20Sopenharmony_ci if (ret) 7368c2ecf20Sopenharmony_ci goto no_need_unlink; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * The enumeration process is as follows; 7408c2ecf20Sopenharmony_ci * 7418c2ecf20Sopenharmony_ci * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) 7428c2ecf20Sopenharmony_ci * to get max packet length of default pipe 7438c2ecf20Sopenharmony_ci * 7448c2ecf20Sopenharmony_ci * 2. Set_Address request to DevAddr(0) EndPoint(0) 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_ci if (usb_pipedevice(urb->pipe) == 0) { 7488c2ecf20Sopenharmony_ci struct usb_device *old; 7498c2ecf20Sopenharmony_ci __u8 type = usb_pipetype(urb->pipe); 7508c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrlreq = 7518c2ecf20Sopenharmony_ci (struct usb_ctrlrequest *) urb->setup_packet; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (type != PIPE_CONTROL || !ctrlreq) { 7548c2ecf20Sopenharmony_ci dev_err(dev, "invalid request to devnum 0\n"); 7558c2ecf20Sopenharmony_ci ret = -EINVAL; 7568c2ecf20Sopenharmony_ci goto no_need_xmit; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci old = vdev->udev; 7608c2ecf20Sopenharmony_ci switch (ctrlreq->bRequest) { 7618c2ecf20Sopenharmony_ci case USB_REQ_SET_ADDRESS: 7628c2ecf20Sopenharmony_ci /* set_address may come when a device is reset */ 7638c2ecf20Sopenharmony_ci dev_info(dev, "SetAddress Request (%d) to port %d\n", 7648c2ecf20Sopenharmony_ci ctrlreq->wValue, vdev->rhport); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci vdev->udev = usb_get_dev(urb->dev); 7678c2ecf20Sopenharmony_ci usb_put_dev(old); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci spin_lock(&vdev->ud.lock); 7708c2ecf20Sopenharmony_ci vdev->ud.status = VDEV_ST_USED; 7718c2ecf20Sopenharmony_ci spin_unlock(&vdev->ud.lock); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (urb->status == -EINPROGRESS) { 7748c2ecf20Sopenharmony_ci /* This request is successfully completed. */ 7758c2ecf20Sopenharmony_ci /* If not -EINPROGRESS, possibly unlinked. */ 7768c2ecf20Sopenharmony_ci urb->status = 0; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci goto no_need_xmit; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci case USB_REQ_GET_DESCRIPTOR: 7828c2ecf20Sopenharmony_ci if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8)) 7838c2ecf20Sopenharmony_ci usbip_dbg_vhci_hc( 7848c2ecf20Sopenharmony_ci "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n"); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci vdev->udev = usb_get_dev(urb->dev); 7878c2ecf20Sopenharmony_ci usb_put_dev(old); 7888c2ecf20Sopenharmony_ci goto out; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci default: 7918c2ecf20Sopenharmony_ci /* NOT REACHED */ 7928c2ecf20Sopenharmony_ci dev_err(dev, 7938c2ecf20Sopenharmony_ci "invalid request to devnum 0 bRequest %u, wValue %u\n", 7948c2ecf20Sopenharmony_ci ctrlreq->bRequest, 7958c2ecf20Sopenharmony_ci ctrlreq->wValue); 7968c2ecf20Sopenharmony_ci ret = -EINVAL; 7978c2ecf20Sopenharmony_ci goto no_need_xmit; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ciout: 8038c2ecf20Sopenharmony_ci vhci_tx_urb(urb, vdev); 8048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return 0; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cino_need_xmit: 8098c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 8108c2ecf20Sopenharmony_cino_need_unlink: 8118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 8128c2ecf20Sopenharmony_ci if (!ret) { 8138c2ecf20Sopenharmony_ci /* usb_hcd_giveback_urb() should be called with 8148c2ecf20Sopenharmony_ci * irqs disabled 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ci local_irq_disable(); 8178c2ecf20Sopenharmony_ci usb_hcd_giveback_urb(hcd, urb, urb->status); 8188c2ecf20Sopenharmony_ci local_irq_enable(); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci/* 8248c2ecf20Sopenharmony_ci * vhci_rx gives back the urb after receiving the reply of the urb. If an 8258c2ecf20Sopenharmony_ci * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives 8268c2ecf20Sopenharmony_ci * back its urb. For the driver unlinking the urb, the content of the urb is 8278c2ecf20Sopenharmony_ci * not important, but the calling to its completion handler is important; the 8288c2ecf20Sopenharmony_ci * completion of unlinking is notified by the completion handler. 8298c2ecf20Sopenharmony_ci * 8308c2ecf20Sopenharmony_ci * 8318c2ecf20Sopenharmony_ci * CLIENT SIDE 8328c2ecf20Sopenharmony_ci * 8338c2ecf20Sopenharmony_ci * - When vhci_hcd receives RET_SUBMIT, 8348c2ecf20Sopenharmony_ci * 8358c2ecf20Sopenharmony_ci * - case 1a). the urb of the pdu is not unlinking. 8368c2ecf20Sopenharmony_ci * - normal case 8378c2ecf20Sopenharmony_ci * => just give back the urb 8388c2ecf20Sopenharmony_ci * 8398c2ecf20Sopenharmony_ci * - case 1b). the urb of the pdu is unlinking. 8408c2ecf20Sopenharmony_ci * - usbip.ko will return a reply of the unlinking request. 8418c2ecf20Sopenharmony_ci * => give back the urb now and go to case 2b). 8428c2ecf20Sopenharmony_ci * 8438c2ecf20Sopenharmony_ci * - When vhci_hcd receives RET_UNLINK, 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci * - case 2a). a submit request is still pending in vhci_hcd. 8468c2ecf20Sopenharmony_ci * - urb was really pending in usbip.ko and urb_unlink_urb() was 8478c2ecf20Sopenharmony_ci * completed there. 8488c2ecf20Sopenharmony_ci * => free a pending submit request 8498c2ecf20Sopenharmony_ci * => notify unlink completeness by giving back the urb 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * - case 2b). a submit request is *not* pending in vhci_hcd. 8528c2ecf20Sopenharmony_ci * - urb was already given back to the core driver. 8538c2ecf20Sopenharmony_ci * => do not give back the urb 8548c2ecf20Sopenharmony_ci * 8558c2ecf20Sopenharmony_ci * 8568c2ecf20Sopenharmony_ci * SERVER SIDE 8578c2ecf20Sopenharmony_ci * 8588c2ecf20Sopenharmony_ci * - When usbip receives CMD_UNLINK, 8598c2ecf20Sopenharmony_ci * 8608c2ecf20Sopenharmony_ci * - case 3a). the urb of the unlink request is now in submission. 8618c2ecf20Sopenharmony_ci * => do usb_unlink_urb(). 8628c2ecf20Sopenharmony_ci * => after the unlink is completed, send RET_UNLINK. 8638c2ecf20Sopenharmony_ci * 8648c2ecf20Sopenharmony_ci * - case 3b). the urb of the unlink request is not in submission. 8658c2ecf20Sopenharmony_ci * - may be already completed or never be received 8668c2ecf20Sopenharmony_ci * => send RET_UNLINK 8678c2ecf20Sopenharmony_ci * 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_cistatic int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); 8728c2ecf20Sopenharmony_ci struct vhci *vhci = vhci_hcd->vhci; 8738c2ecf20Sopenharmony_ci struct vhci_priv *priv; 8748c2ecf20Sopenharmony_ci struct vhci_device *vdev; 8758c2ecf20Sopenharmony_ci unsigned long flags; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci priv = urb->hcpriv; 8808c2ecf20Sopenharmony_ci if (!priv) { 8818c2ecf20Sopenharmony_ci /* URB was never linked! or will be soon given back by 8828c2ecf20Sopenharmony_ci * vhci_rx. */ 8838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 8848c2ecf20Sopenharmony_ci return -EIDRM; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci { 8888c2ecf20Sopenharmony_ci int ret = 0; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci ret = usb_hcd_check_unlink_urb(hcd, urb, status); 8918c2ecf20Sopenharmony_ci if (ret) { 8928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 8938c2ecf20Sopenharmony_ci return ret; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* send unlink request here? */ 8988c2ecf20Sopenharmony_ci vdev = priv->vdev; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (!vdev->ud.tcp_socket) { 9018c2ecf20Sopenharmony_ci /* tcp connection is closed */ 9028c2ecf20Sopenharmony_ci spin_lock(&vdev->priv_lock); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci list_del(&priv->list); 9058c2ecf20Sopenharmony_ci kfree(priv); 9068c2ecf20Sopenharmony_ci urb->hcpriv = NULL; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci spin_unlock(&vdev->priv_lock); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* 9118c2ecf20Sopenharmony_ci * If tcp connection is alive, we have sent CMD_UNLINK. 9128c2ecf20Sopenharmony_ci * vhci_rx will receive RET_UNLINK and give back the URB. 9138c2ecf20Sopenharmony_ci * Otherwise, we give back it here. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 9188c2ecf20Sopenharmony_ci usb_hcd_giveback_urb(hcd, urb, urb->status); 9198c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci } else { 9228c2ecf20Sopenharmony_ci /* tcp connection is alive */ 9238c2ecf20Sopenharmony_ci struct vhci_unlink *unlink; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci spin_lock(&vdev->priv_lock); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* setup CMD_UNLINK pdu */ 9288c2ecf20Sopenharmony_ci unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); 9298c2ecf20Sopenharmony_ci if (!unlink) { 9308c2ecf20Sopenharmony_ci spin_unlock(&vdev->priv_lock); 9318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 9328c2ecf20Sopenharmony_ci usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); 9338c2ecf20Sopenharmony_ci return -ENOMEM; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci unlink->seqnum = atomic_inc_return(&vhci_hcd->seqnum); 9378c2ecf20Sopenharmony_ci if (unlink->seqnum == 0xffff) 9388c2ecf20Sopenharmony_ci pr_info("seqnum max\n"); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci unlink->unlink_seqnum = priv->seqnum; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci /* send cmd_unlink and try to cancel the pending URB in the 9438c2ecf20Sopenharmony_ci * peer */ 9448c2ecf20Sopenharmony_ci list_add_tail(&unlink->list, &vdev->unlink_tx); 9458c2ecf20Sopenharmony_ci wake_up(&vdev->waitq_tx); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci spin_unlock(&vdev->priv_lock); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci usbip_dbg_vhci_hc("leave\n"); 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic void vhci_device_unlink_cleanup(struct vhci_device *vdev) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); 9598c2ecf20Sopenharmony_ci struct usb_hcd *hcd = vhci_hcd_to_hcd(vhci_hcd); 9608c2ecf20Sopenharmony_ci struct vhci *vhci = vhci_hcd->vhci; 9618c2ecf20Sopenharmony_ci struct vhci_unlink *unlink, *tmp; 9628c2ecf20Sopenharmony_ci unsigned long flags; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 9658c2ecf20Sopenharmony_ci spin_lock(&vdev->priv_lock); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { 9688c2ecf20Sopenharmony_ci struct urb *urb; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci /* give back urb of unsent unlink request */ 9718c2ecf20Sopenharmony_ci pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); 9748c2ecf20Sopenharmony_ci if (!urb) { 9758c2ecf20Sopenharmony_ci list_del(&unlink->list); 9768c2ecf20Sopenharmony_ci kfree(unlink); 9778c2ecf20Sopenharmony_ci continue; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci urb->status = -ENODEV; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci list_del(&unlink->list); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci spin_unlock(&vdev->priv_lock); 9878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci usb_hcd_giveback_urb(hcd, urb, urb->status); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 9928c2ecf20Sopenharmony_ci spin_lock(&vdev->priv_lock); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci kfree(unlink); 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci while (!list_empty(&vdev->unlink_rx)) { 9988c2ecf20Sopenharmony_ci struct urb *urb; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, 10018c2ecf20Sopenharmony_ci list); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* give back URB of unanswered unlink request */ 10048c2ecf20Sopenharmony_ci pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); 10078c2ecf20Sopenharmony_ci if (!urb) { 10088c2ecf20Sopenharmony_ci pr_info("the urb (seqnum %lu) was already given back\n", 10098c2ecf20Sopenharmony_ci unlink->unlink_seqnum); 10108c2ecf20Sopenharmony_ci list_del(&unlink->list); 10118c2ecf20Sopenharmony_ci kfree(unlink); 10128c2ecf20Sopenharmony_ci continue; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci urb->status = -ENODEV; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci list_del(&unlink->list); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci spin_unlock(&vdev->priv_lock); 10228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci usb_hcd_giveback_urb(hcd, urb, urb->status); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 10278c2ecf20Sopenharmony_ci spin_lock(&vdev->priv_lock); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci kfree(unlink); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci spin_unlock(&vdev->priv_lock); 10338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci/* 10378c2ecf20Sopenharmony_ci * The important thing is that only one context begins cleanup. 10388c2ecf20Sopenharmony_ci * This is why error handling and cleanup become simple. 10398c2ecf20Sopenharmony_ci * We do not want to consider race condition as possible. 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_cistatic void vhci_shutdown_connection(struct usbip_device *ud) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* need this? see stub_dev.c */ 10468c2ecf20Sopenharmony_ci if (ud->tcp_socket) { 10478c2ecf20Sopenharmony_ci pr_debug("shutdown tcp_socket %d\n", ud->sockfd); 10488c2ecf20Sopenharmony_ci kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* kill threads related to this sdev */ 10528c2ecf20Sopenharmony_ci if (vdev->ud.tcp_rx) { 10538c2ecf20Sopenharmony_ci kthread_stop_put(vdev->ud.tcp_rx); 10548c2ecf20Sopenharmony_ci vdev->ud.tcp_rx = NULL; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci if (vdev->ud.tcp_tx) { 10578c2ecf20Sopenharmony_ci kthread_stop_put(vdev->ud.tcp_tx); 10588c2ecf20Sopenharmony_ci vdev->ud.tcp_tx = NULL; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci pr_info("stop threads\n"); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* active connection is closed */ 10638c2ecf20Sopenharmony_ci if (vdev->ud.tcp_socket) { 10648c2ecf20Sopenharmony_ci sockfd_put(vdev->ud.tcp_socket); 10658c2ecf20Sopenharmony_ci vdev->ud.tcp_socket = NULL; 10668c2ecf20Sopenharmony_ci vdev->ud.sockfd = -1; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci pr_info("release socket\n"); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci vhci_device_unlink_cleanup(vdev); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* 10738c2ecf20Sopenharmony_ci * rh_port_disconnect() is a trigger of ... 10748c2ecf20Sopenharmony_ci * usb_disable_device(): 10758c2ecf20Sopenharmony_ci * disable all the endpoints for a USB device. 10768c2ecf20Sopenharmony_ci * usb_disable_endpoint(): 10778c2ecf20Sopenharmony_ci * disable endpoints. pending urbs are unlinked(dequeued). 10788c2ecf20Sopenharmony_ci * 10798c2ecf20Sopenharmony_ci * NOTE: After calling rh_port_disconnect(), the USB device drivers of a 10808c2ecf20Sopenharmony_ci * detached device should release used urbs in a cleanup function (i.e. 10818c2ecf20Sopenharmony_ci * xxx_disconnect()). Therefore, vhci_hcd does not need to release 10828c2ecf20Sopenharmony_ci * pushed urbs and their private data in this function. 10838c2ecf20Sopenharmony_ci * 10848c2ecf20Sopenharmony_ci * NOTE: vhci_dequeue() must be considered carefully. When shutting down 10858c2ecf20Sopenharmony_ci * a connection, vhci_shutdown_connection() expects vhci_dequeue() 10868c2ecf20Sopenharmony_ci * gives back pushed urbs and frees their private data by request of 10878c2ecf20Sopenharmony_ci * the cleanup function of a USB driver. When unlinking a urb with an 10888c2ecf20Sopenharmony_ci * active connection, vhci_dequeue() does not give back the urb which 10898c2ecf20Sopenharmony_ci * is actually given back by vhci_rx after receiving its return pdu. 10908c2ecf20Sopenharmony_ci * 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ci rh_port_disconnect(vdev); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci pr_info("disconnect device\n"); 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic void vhci_device_reset(struct usbip_device *ud) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); 11008c2ecf20Sopenharmony_ci struct usb_device *old = vdev->udev; 11018c2ecf20Sopenharmony_ci unsigned long flags; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci spin_lock_irqsave(&ud->lock, flags); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci vdev->speed = 0; 11068c2ecf20Sopenharmony_ci vdev->devid = 0; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci vdev->udev = NULL; 11098c2ecf20Sopenharmony_ci usb_put_dev(old); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (ud->tcp_socket) { 11128c2ecf20Sopenharmony_ci sockfd_put(ud->tcp_socket); 11138c2ecf20Sopenharmony_ci ud->tcp_socket = NULL; 11148c2ecf20Sopenharmony_ci ud->sockfd = -1; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci ud->status = VDEV_ST_NULL; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ud->lock, flags); 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic void vhci_device_unusable(struct usbip_device *ud) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci unsigned long flags; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci spin_lock_irqsave(&ud->lock, flags); 11268c2ecf20Sopenharmony_ci ud->status = VDEV_ST_ERROR; 11278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ud->lock, flags); 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void vhci_device_init(struct vhci_device *vdev) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci memset(vdev, 0, sizeof(struct vhci_device)); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci vdev->ud.side = USBIP_VHCI; 11358c2ecf20Sopenharmony_ci vdev->ud.status = VDEV_ST_NULL; 11368c2ecf20Sopenharmony_ci spin_lock_init(&vdev->ud.lock); 11378c2ecf20Sopenharmony_ci mutex_init(&vdev->ud.sysfs_lock); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vdev->priv_rx); 11408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vdev->priv_tx); 11418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vdev->unlink_tx); 11428c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vdev->unlink_rx); 11438c2ecf20Sopenharmony_ci spin_lock_init(&vdev->priv_lock); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci init_waitqueue_head(&vdev->waitq_tx); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci vdev->ud.eh_ops.shutdown = vhci_shutdown_connection; 11488c2ecf20Sopenharmony_ci vdev->ud.eh_ops.reset = vhci_device_reset; 11498c2ecf20Sopenharmony_ci vdev->ud.eh_ops.unusable = vhci_device_unusable; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci usbip_start_eh(&vdev->ud); 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int hcd_name_to_id(const char *name) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci char *c; 11578c2ecf20Sopenharmony_ci long val; 11588c2ecf20Sopenharmony_ci int ret; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci c = strchr(name, '.'); 11618c2ecf20Sopenharmony_ci if (c == NULL) 11628c2ecf20Sopenharmony_ci return 0; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci ret = kstrtol(c+1, 10, &val); 11658c2ecf20Sopenharmony_ci if (ret < 0) 11668c2ecf20Sopenharmony_ci return ret; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci return val; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic int vhci_setup(struct usb_hcd *hcd) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct vhci *vhci = *((void **)dev_get_platdata(hcd->self.controller)); 11748c2ecf20Sopenharmony_ci if (usb_hcd_is_primary_hcd(hcd)) { 11758c2ecf20Sopenharmony_ci vhci->vhci_hcd_hs = hcd_to_vhci_hcd(hcd); 11768c2ecf20Sopenharmony_ci vhci->vhci_hcd_hs->vhci = vhci; 11778c2ecf20Sopenharmony_ci /* 11788c2ecf20Sopenharmony_ci * Mark the first roothub as being USB 2.0. 11798c2ecf20Sopenharmony_ci * The USB 3.0 roothub will be registered later by 11808c2ecf20Sopenharmony_ci * vhci_hcd_probe() 11818c2ecf20Sopenharmony_ci */ 11828c2ecf20Sopenharmony_ci hcd->speed = HCD_USB2; 11838c2ecf20Sopenharmony_ci hcd->self.root_hub->speed = USB_SPEED_HIGH; 11848c2ecf20Sopenharmony_ci } else { 11858c2ecf20Sopenharmony_ci vhci->vhci_hcd_ss = hcd_to_vhci_hcd(hcd); 11868c2ecf20Sopenharmony_ci vhci->vhci_hcd_ss->vhci = vhci; 11878c2ecf20Sopenharmony_ci hcd->speed = HCD_USB3; 11888c2ecf20Sopenharmony_ci hcd->self.root_hub->speed = USB_SPEED_SUPER; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * Support SG. 11938c2ecf20Sopenharmony_ci * sg_tablesize is an arbitrary value to alleviate memory pressure 11948c2ecf20Sopenharmony_ci * on the host. 11958c2ecf20Sopenharmony_ci */ 11968c2ecf20Sopenharmony_ci hcd->self.sg_tablesize = 32; 11978c2ecf20Sopenharmony_ci hcd->self.no_sg_constraint = 1; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci return 0; 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic int vhci_start(struct usb_hcd *hcd) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); 12058c2ecf20Sopenharmony_ci int id, rhport; 12068c2ecf20Sopenharmony_ci int err; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci usbip_dbg_vhci_hc("enter vhci_start\n"); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (usb_hcd_is_primary_hcd(hcd)) 12118c2ecf20Sopenharmony_ci spin_lock_init(&vhci_hcd->vhci->lock); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* initialize private data of usb_hcd */ 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { 12168c2ecf20Sopenharmony_ci struct vhci_device *vdev = &vhci_hcd->vdev[rhport]; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci vhci_device_init(vdev); 12198c2ecf20Sopenharmony_ci vdev->rhport = rhport; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci atomic_set(&vhci_hcd->seqnum, 0); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci hcd->power_budget = 0; /* no limit */ 12258c2ecf20Sopenharmony_ci hcd->uses_new_polling = 1; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_OTG 12288c2ecf20Sopenharmony_ci hcd->self.otg_port = 1; 12298c2ecf20Sopenharmony_ci#endif 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci id = hcd_name_to_id(hcd_name(hcd)); 12328c2ecf20Sopenharmony_ci if (id < 0) { 12338c2ecf20Sopenharmony_ci pr_err("invalid vhci name %s\n", hcd_name(hcd)); 12348c2ecf20Sopenharmony_ci return -EINVAL; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* vhci_hcd is now ready to be controlled through sysfs */ 12388c2ecf20Sopenharmony_ci if (id == 0 && usb_hcd_is_primary_hcd(hcd)) { 12398c2ecf20Sopenharmony_ci err = vhci_init_attr_group(); 12408c2ecf20Sopenharmony_ci if (err) { 12418c2ecf20Sopenharmony_ci dev_err(hcd_dev(hcd), "init attr group failed, err = %d\n", err); 12428c2ecf20Sopenharmony_ci return err; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci err = sysfs_create_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); 12458c2ecf20Sopenharmony_ci if (err) { 12468c2ecf20Sopenharmony_ci dev_err(hcd_dev(hcd), "create sysfs files failed, err = %d\n", err); 12478c2ecf20Sopenharmony_ci vhci_finish_attr_group(); 12488c2ecf20Sopenharmony_ci return err; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci pr_info("created sysfs %s\n", hcd_name(hcd)); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci return 0; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic void vhci_stop(struct usb_hcd *hcd) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); 12598c2ecf20Sopenharmony_ci int id, rhport; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci usbip_dbg_vhci_hc("stop VHCI controller\n"); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* 1. remove the userland interface of vhci_hcd */ 12648c2ecf20Sopenharmony_ci id = hcd_name_to_id(hcd_name(hcd)); 12658c2ecf20Sopenharmony_ci if (id == 0 && usb_hcd_is_primary_hcd(hcd)) { 12668c2ecf20Sopenharmony_ci sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); 12678c2ecf20Sopenharmony_ci vhci_finish_attr_group(); 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci /* 2. shutdown all the ports of vhci_hcd */ 12718c2ecf20Sopenharmony_ci for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { 12728c2ecf20Sopenharmony_ci struct vhci_device *vdev = &vhci_hcd->vdev[rhport]; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); 12758c2ecf20Sopenharmony_ci usbip_stop_eh(&vdev->ud); 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic int vhci_get_frame_number(struct usb_hcd *hcd) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci dev_err_ratelimited(&hcd->self.root_hub->dev, "Not yet implemented\n"); 12828c2ecf20Sopenharmony_ci return 0; 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci/* FIXME: suspend/resume */ 12888c2ecf20Sopenharmony_cistatic int vhci_bus_suspend(struct usb_hcd *hcd) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct vhci *vhci = *((void **)dev_get_platdata(hcd->self.controller)); 12918c2ecf20Sopenharmony_ci unsigned long flags; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 12968c2ecf20Sopenharmony_ci hcd->state = HC_STATE_SUSPENDED; 12978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci return 0; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic int vhci_bus_resume(struct usb_hcd *hcd) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci struct vhci *vhci = *((void **)dev_get_platdata(hcd->self.controller)); 13058c2ecf20Sopenharmony_ci int rc = 0; 13068c2ecf20Sopenharmony_ci unsigned long flags; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 13118c2ecf20Sopenharmony_ci if (!HCD_HW_ACCESSIBLE(hcd)) 13128c2ecf20Sopenharmony_ci rc = -ESHUTDOWN; 13138c2ecf20Sopenharmony_ci else 13148c2ecf20Sopenharmony_ci hcd->state = HC_STATE_RUNNING; 13158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci return rc; 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci#else 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci#define vhci_bus_suspend NULL 13238c2ecf20Sopenharmony_ci#define vhci_bus_resume NULL 13248c2ecf20Sopenharmony_ci#endif 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/* Change a group of bulk endpoints to support multiple stream IDs */ 13278c2ecf20Sopenharmony_cistatic int vhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, 13288c2ecf20Sopenharmony_ci struct usb_host_endpoint **eps, unsigned int num_eps, 13298c2ecf20Sopenharmony_ci unsigned int num_streams, gfp_t mem_flags) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci dev_dbg(&hcd->self.root_hub->dev, "vhci_alloc_streams not implemented\n"); 13328c2ecf20Sopenharmony_ci return 0; 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci/* Reverts a group of bulk endpoints back to not using stream IDs. */ 13368c2ecf20Sopenharmony_cistatic int vhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, 13378c2ecf20Sopenharmony_ci struct usb_host_endpoint **eps, unsigned int num_eps, 13388c2ecf20Sopenharmony_ci gfp_t mem_flags) 13398c2ecf20Sopenharmony_ci{ 13408c2ecf20Sopenharmony_ci dev_dbg(&hcd->self.root_hub->dev, "vhci_free_streams not implemented\n"); 13418c2ecf20Sopenharmony_ci return 0; 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_cistatic const struct hc_driver vhci_hc_driver = { 13458c2ecf20Sopenharmony_ci .description = driver_name, 13468c2ecf20Sopenharmony_ci .product_desc = driver_desc, 13478c2ecf20Sopenharmony_ci .hcd_priv_size = sizeof(struct vhci_hcd), 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci .flags = HCD_USB3 | HCD_SHARED, 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci .reset = vhci_setup, 13528c2ecf20Sopenharmony_ci .start = vhci_start, 13538c2ecf20Sopenharmony_ci .stop = vhci_stop, 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci .urb_enqueue = vhci_urb_enqueue, 13568c2ecf20Sopenharmony_ci .urb_dequeue = vhci_urb_dequeue, 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci .get_frame_number = vhci_get_frame_number, 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci .hub_status_data = vhci_hub_status, 13618c2ecf20Sopenharmony_ci .hub_control = vhci_hub_control, 13628c2ecf20Sopenharmony_ci .bus_suspend = vhci_bus_suspend, 13638c2ecf20Sopenharmony_ci .bus_resume = vhci_bus_resume, 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci .alloc_streams = vhci_alloc_streams, 13668c2ecf20Sopenharmony_ci .free_streams = vhci_free_streams, 13678c2ecf20Sopenharmony_ci}; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic int vhci_hcd_probe(struct platform_device *pdev) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci struct vhci *vhci = *((void **)dev_get_platdata(&pdev->dev)); 13728c2ecf20Sopenharmony_ci struct usb_hcd *hcd_hs; 13738c2ecf20Sopenharmony_ci struct usb_hcd *hcd_ss; 13748c2ecf20Sopenharmony_ci int ret; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* 13798c2ecf20Sopenharmony_ci * Allocate and initialize hcd. 13808c2ecf20Sopenharmony_ci * Our private data is also allocated automatically. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_ci hcd_hs = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); 13838c2ecf20Sopenharmony_ci if (!hcd_hs) { 13848c2ecf20Sopenharmony_ci pr_err("create primary hcd failed\n"); 13858c2ecf20Sopenharmony_ci return -ENOMEM; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci hcd_hs->has_tt = 1; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci /* 13908c2ecf20Sopenharmony_ci * Finish generic HCD structure initialization and register. 13918c2ecf20Sopenharmony_ci * Call the driver's reset() and start() routines. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_ci ret = usb_add_hcd(hcd_hs, 0, 0); 13948c2ecf20Sopenharmony_ci if (ret != 0) { 13958c2ecf20Sopenharmony_ci pr_err("usb_add_hcd hs failed %d\n", ret); 13968c2ecf20Sopenharmony_ci goto put_usb2_hcd; 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci hcd_ss = usb_create_shared_hcd(&vhci_hc_driver, &pdev->dev, 14008c2ecf20Sopenharmony_ci dev_name(&pdev->dev), hcd_hs); 14018c2ecf20Sopenharmony_ci if (!hcd_ss) { 14028c2ecf20Sopenharmony_ci ret = -ENOMEM; 14038c2ecf20Sopenharmony_ci pr_err("create shared hcd failed\n"); 14048c2ecf20Sopenharmony_ci goto remove_usb2_hcd; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci ret = usb_add_hcd(hcd_ss, 0, 0); 14088c2ecf20Sopenharmony_ci if (ret) { 14098c2ecf20Sopenharmony_ci pr_err("usb_add_hcd ss failed %d\n", ret); 14108c2ecf20Sopenharmony_ci goto put_usb3_hcd; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci usbip_dbg_vhci_hc("bye\n"); 14148c2ecf20Sopenharmony_ci return 0; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ciput_usb3_hcd: 14178c2ecf20Sopenharmony_ci usb_put_hcd(hcd_ss); 14188c2ecf20Sopenharmony_ciremove_usb2_hcd: 14198c2ecf20Sopenharmony_ci usb_remove_hcd(hcd_hs); 14208c2ecf20Sopenharmony_ciput_usb2_hcd: 14218c2ecf20Sopenharmony_ci usb_put_hcd(hcd_hs); 14228c2ecf20Sopenharmony_ci vhci->vhci_hcd_hs = NULL; 14238c2ecf20Sopenharmony_ci vhci->vhci_hcd_ss = NULL; 14248c2ecf20Sopenharmony_ci return ret; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic int vhci_hcd_remove(struct platform_device *pdev) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci struct vhci *vhci = *((void **)dev_get_platdata(&pdev->dev)); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* 14328c2ecf20Sopenharmony_ci * Disconnects the root hub, 14338c2ecf20Sopenharmony_ci * then reverses the effects of usb_add_hcd(), 14348c2ecf20Sopenharmony_ci * invoking the HCD's stop() methods. 14358c2ecf20Sopenharmony_ci */ 14368c2ecf20Sopenharmony_ci usb_remove_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_ss)); 14378c2ecf20Sopenharmony_ci usb_put_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_ss)); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci usb_remove_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_hs)); 14408c2ecf20Sopenharmony_ci usb_put_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_hs)); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci vhci->vhci_hcd_hs = NULL; 14438c2ecf20Sopenharmony_ci vhci->vhci_hcd_ss = NULL; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci return 0; 14468c2ecf20Sopenharmony_ci} 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci/* what should happen for USB/IP under suspend/resume? */ 14518c2ecf20Sopenharmony_cistatic int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct usb_hcd *hcd; 14548c2ecf20Sopenharmony_ci struct vhci *vhci; 14558c2ecf20Sopenharmony_ci int rhport; 14568c2ecf20Sopenharmony_ci int connected = 0; 14578c2ecf20Sopenharmony_ci int ret = 0; 14588c2ecf20Sopenharmony_ci unsigned long flags; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s\n", __func__); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci hcd = platform_get_drvdata(pdev); 14638c2ecf20Sopenharmony_ci if (!hcd) 14648c2ecf20Sopenharmony_ci return 0; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci vhci = *((void **)dev_get_platdata(hcd->self.controller)); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhci->lock, flags); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { 14718c2ecf20Sopenharmony_ci if (vhci->vhci_hcd_hs->port_status[rhport] & 14728c2ecf20Sopenharmony_ci USB_PORT_STAT_CONNECTION) 14738c2ecf20Sopenharmony_ci connected += 1; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci if (vhci->vhci_hcd_ss->port_status[rhport] & 14768c2ecf20Sopenharmony_ci USB_PORT_STAT_CONNECTION) 14778c2ecf20Sopenharmony_ci connected += 1; 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhci->lock, flags); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci if (connected > 0) { 14838c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 14848c2ecf20Sopenharmony_ci "We have %d active connection%s. Do not suspend.\n", 14858c2ecf20Sopenharmony_ci connected, (connected == 1 ? "" : "s")); 14868c2ecf20Sopenharmony_ci ret = -EBUSY; 14878c2ecf20Sopenharmony_ci } else { 14888c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "suspend vhci_hcd"); 14898c2ecf20Sopenharmony_ci clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci return ret; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic int vhci_hcd_resume(struct platform_device *pdev) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct usb_hcd *hcd; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s\n", __func__); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci hcd = platform_get_drvdata(pdev); 15028c2ecf20Sopenharmony_ci if (!hcd) 15038c2ecf20Sopenharmony_ci return 0; 15048c2ecf20Sopenharmony_ci set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 15058c2ecf20Sopenharmony_ci usb_hcd_poll_rh_status(hcd); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return 0; 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci#else 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci#define vhci_hcd_suspend NULL 15138c2ecf20Sopenharmony_ci#define vhci_hcd_resume NULL 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci#endif 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_cistatic struct platform_driver vhci_driver = { 15188c2ecf20Sopenharmony_ci .probe = vhci_hcd_probe, 15198c2ecf20Sopenharmony_ci .remove = vhci_hcd_remove, 15208c2ecf20Sopenharmony_ci .suspend = vhci_hcd_suspend, 15218c2ecf20Sopenharmony_ci .resume = vhci_hcd_resume, 15228c2ecf20Sopenharmony_ci .driver = { 15238c2ecf20Sopenharmony_ci .name = driver_name, 15248c2ecf20Sopenharmony_ci }, 15258c2ecf20Sopenharmony_ci}; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic void del_platform_devices(void) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct platform_device *pdev; 15308c2ecf20Sopenharmony_ci int i; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci for (i = 0; i < vhci_num_controllers; i++) { 15338c2ecf20Sopenharmony_ci pdev = vhcis[i].pdev; 15348c2ecf20Sopenharmony_ci if (pdev != NULL) 15358c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 15368c2ecf20Sopenharmony_ci vhcis[i].pdev = NULL; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci sysfs_remove_link(&platform_bus.kobj, driver_name); 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_cistatic int __init vhci_hcd_init(void) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci int i, ret; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (usb_disabled()) 15468c2ecf20Sopenharmony_ci return -ENODEV; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (vhci_num_controllers < 1) 15498c2ecf20Sopenharmony_ci vhci_num_controllers = 1; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci vhcis = kcalloc(vhci_num_controllers, sizeof(struct vhci), GFP_KERNEL); 15528c2ecf20Sopenharmony_ci if (vhcis == NULL) 15538c2ecf20Sopenharmony_ci return -ENOMEM; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci for (i = 0; i < vhci_num_controllers; i++) { 15568c2ecf20Sopenharmony_ci vhcis[i].pdev = platform_device_alloc(driver_name, i); 15578c2ecf20Sopenharmony_ci if (!vhcis[i].pdev) { 15588c2ecf20Sopenharmony_ci i--; 15598c2ecf20Sopenharmony_ci while (i >= 0) 15608c2ecf20Sopenharmony_ci platform_device_put(vhcis[i--].pdev); 15618c2ecf20Sopenharmony_ci ret = -ENOMEM; 15628c2ecf20Sopenharmony_ci goto err_device_alloc; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci for (i = 0; i < vhci_num_controllers; i++) { 15668c2ecf20Sopenharmony_ci void *vhci = &vhcis[i]; 15678c2ecf20Sopenharmony_ci ret = platform_device_add_data(vhcis[i].pdev, &vhci, sizeof(void *)); 15688c2ecf20Sopenharmony_ci if (ret) 15698c2ecf20Sopenharmony_ci goto err_driver_register; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci ret = platform_driver_register(&vhci_driver); 15738c2ecf20Sopenharmony_ci if (ret) 15748c2ecf20Sopenharmony_ci goto err_driver_register; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci for (i = 0; i < vhci_num_controllers; i++) { 15778c2ecf20Sopenharmony_ci ret = platform_device_add(vhcis[i].pdev); 15788c2ecf20Sopenharmony_ci if (ret < 0) { 15798c2ecf20Sopenharmony_ci i--; 15808c2ecf20Sopenharmony_ci while (i >= 0) 15818c2ecf20Sopenharmony_ci platform_device_del(vhcis[i--].pdev); 15828c2ecf20Sopenharmony_ci goto err_add_hcd; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci return ret; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cierr_add_hcd: 15898c2ecf20Sopenharmony_ci platform_driver_unregister(&vhci_driver); 15908c2ecf20Sopenharmony_cierr_driver_register: 15918c2ecf20Sopenharmony_ci for (i = 0; i < vhci_num_controllers; i++) 15928c2ecf20Sopenharmony_ci platform_device_put(vhcis[i].pdev); 15938c2ecf20Sopenharmony_cierr_device_alloc: 15948c2ecf20Sopenharmony_ci kfree(vhcis); 15958c2ecf20Sopenharmony_ci return ret; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic void __exit vhci_hcd_exit(void) 15998c2ecf20Sopenharmony_ci{ 16008c2ecf20Sopenharmony_ci del_platform_devices(); 16018c2ecf20Sopenharmony_ci platform_driver_unregister(&vhci_driver); 16028c2ecf20Sopenharmony_ci kfree(vhcis); 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_cimodule_init(vhci_hcd_init); 16068c2ecf20Sopenharmony_cimodule_exit(vhci_hcd_exit); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 16098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 16108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1611