Commit 1c9de5bf authored by Yuyang Du's avatar Yuyang Du Committed by Greg Kroah-Hartman

usbip: vhci-hcd: Add USB3 SuperSpeed support

This patch adds a USB3 HCD to an existing USB2 HCD and provides
the support of SuperSpeed, in case the device can only be enumerated
with SuperSpeed.

The bulk of the added code in usb3_bos_desc and hub_control to support
SuperSpeed is borrowed from the commit 1cd8fd28 ("usb: gadget:
dummy_hcd: add SuperSpeed support").

With this patch, each vhci will have VHCI_HC_PORTS HighSpeed ports
and VHCI_HC_PORTS SuperSpeed ports.
Suggested-by: default avatarKrzysztof Opasiak <k.opasiak@samsung.com>
Signed-off-by: default avatarYuyang Du <yuyang.du@intel.com>
Acked-by: default avatarShuah Khan <shuahkh@osg.samsung.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 03cd00d5
......@@ -72,6 +72,11 @@ struct vhci_unlink {
unsigned long unlink_seqnum;
};
enum hub_speed {
HUB_SPEED_HIGH = 0,
HUB_SPEED_SUPER,
};
/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
#ifdef CONFIG_USBIP_VHCI_HC_PORTS
#define VHCI_HC_PORTS CONFIG_USBIP_VHCI_HC_PORTS
......@@ -140,7 +145,7 @@ static inline __u32 port_to_rhport(__u32 port)
static inline int port_to_pdev_nr(__u32 port)
{
return port / VHCI_HC_PORTS;
return port / (VHCI_HC_PORTS * 2);
}
static inline struct vhci_hcd *hcd_to_vhci_hcd(struct usb_hcd *hcd)
......
......@@ -232,6 +232,40 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
return changed ? retval : 0;
}
/* usb 3.0 root hub device descriptor */
static struct {
struct usb_bos_descriptor bos;
struct usb_ss_cap_descriptor ss_cap;
} __packed usb3_bos_desc = {
.bos = {
.bLength = USB_DT_BOS_SIZE,
.bDescriptorType = USB_DT_BOS,
.wTotalLength = cpu_to_le16(sizeof(usb3_bos_desc)),
.bNumDeviceCaps = 1,
},
.ss_cap = {
.bLength = USB_DT_USB_SS_CAP_SIZE,
.bDescriptorType = USB_DT_DEVICE_CAPABILITY,
.bDevCapabilityType = USB_SS_CAP_TYPE,
.wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION),
.bFunctionalitySupport = ilog2(USB_5GBPS_OPERATION),
},
};
static inline void
ss_hub_descriptor(struct usb_hub_descriptor *desc)
{
memset(desc, 0, sizeof *desc);
desc->bDescriptorType = USB_DT_SS_HUB;
desc->bDescLength = 12;
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = VHCI_HC_PORTS;
desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
desc->u.ss.DeviceRemovable = 0xffff;
}
static inline void hub_descriptor(struct usb_hub_descriptor *desc)
{
int width;
......@@ -265,13 +299,15 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/*
* NOTE:
* wIndex shows the port number and begins from 1.
* wIndex (bits 0-7) shows the port number and begins from 1?
*/
wIndex = ((__u8)(wIndex & 0x00ff));
usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
wIndex);
if (wIndex > VHCI_HC_PORTS)
pr_err("invalid port number %d\n", wIndex);
rhport = ((__u8)(wIndex & 0x00ff)) - 1;
rhport = wIndex - 1;
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
......@@ -291,34 +327,26 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed == HCD_USB3) {
pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
}
usbip_dbg_vhci_rh(
" ClearPortFeature: USB_PORT_FEAT_SUSPEND\n");
if (vhci_hcd->port_status[rhport] & USB_PORT_STAT_SUSPEND) {
/* 20msec signaling */
vhci_hcd->resuming = 1;
vhci_hcd->re_timeout =
jiffies + msecs_to_jiffies(20);
vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(20);
}
break;
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" ClearPortFeature: USB_PORT_FEAT_POWER\n");
vhci_hcd->port_status[rhport] = 0;
vhci_hcd->resuming = 0;
break;
case USB_PORT_FEAT_C_RESET:
usbip_dbg_vhci_rh(
" ClearPortFeature: USB_PORT_FEAT_C_RESET\n");
switch (vhci_hcd->vdev[rhport].speed) {
case USB_SPEED_HIGH:
vhci_hcd->port_status[rhport] |=
USB_PORT_STAT_HIGH_SPEED;
break;
case USB_SPEED_LOW:
vhci_hcd->port_status[rhport] |=
USB_PORT_STAT_LOW_SPEED;
break;
default:
break;
}
if (hcd->speed == HCD_USB3)
vhci_hcd->port_status[rhport] &= ~USB_SS_PORT_STAT_POWER;
else
vhci_hcd->port_status[rhport] &= ~USB_PORT_STAT_POWER;
break;
default:
usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
......@@ -329,7 +357,26 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetHubDescriptor:
usbip_dbg_vhci_rh(" GetHubDescriptor\n");
hub_descriptor((struct usb_hub_descriptor *) buf);
if (hcd->speed == HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n");
goto error;
}
if (hcd->speed == HCD_USB3)
ss_hub_descriptor((struct usb_hub_descriptor *) buf);
else
hub_descriptor((struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
if (hcd->speed != HCD_USB3)
goto error;
if ((wValue >> 8) != USB_DT_BOS)
goto error;
memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc));
retval = sizeof(usb3_bos_desc);
break;
case GetHubStatus:
usbip_dbg_vhci_rh(" GetHubStatus\n");
......@@ -337,7 +384,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
if (wIndex > VHCI_HC_PORTS || wIndex < 1) {
if (wIndex < 1) {
pr_err("invalid port number %d\n", wIndex);
retval = -EPIPE;
}
......@@ -348,20 +395,16 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* complete it!!
*/
if (vhci_hcd->resuming && time_after(jiffies, vhci_hcd->re_timeout)) {
vhci_hcd->port_status[rhport] |=
(1 << USB_PORT_FEAT_C_SUSPEND);
vhci_hcd->port_status[rhport] &=
~(1 << USB_PORT_FEAT_SUSPEND);
vhci_hcd->port_status[rhport] |= (1 << USB_PORT_FEAT_C_SUSPEND);
vhci_hcd->port_status[rhport] &= ~(1 << USB_PORT_FEAT_SUSPEND);
vhci_hcd->resuming = 0;
vhci_hcd->re_timeout = 0;
}
if ((vhci_hcd->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) !=
0 && time_after(jiffies, vhci_hcd->re_timeout)) {
vhci_hcd->port_status[rhport] |=
(1 << USB_PORT_FEAT_C_RESET);
vhci_hcd->port_status[rhport] &=
~(1 << USB_PORT_FEAT_RESET);
vhci_hcd->port_status[rhport] |= (1 << USB_PORT_FEAT_C_RESET);
vhci_hcd->port_status[rhport] &= ~(1 << USB_PORT_FEAT_RESET);
vhci_hcd->re_timeout = 0;
if (vhci_hcd->vdev[rhport].ud.status ==
......@@ -373,6 +416,22 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
vhci_hcd->port_status[rhport] |=
USB_PORT_STAT_ENABLE;
}
if (hcd->speed < HCD_USB3) {
switch (vhci_hcd->vdev[rhport].speed) {
case USB_SPEED_HIGH:
vhci_hcd->port_status[rhport] |=
USB_PORT_STAT_HIGH_SPEED;
break;
case USB_SPEED_LOW:
vhci_hcd->port_status[rhport] |=
USB_PORT_STAT_LOW_SPEED;
break;
default:
pr_err("vhci_device speed not set\n");
break;
}
}
}
((__le16 *) buf)[0] = cpu_to_le16(vhci_hcd->port_status[rhport]);
((__le16 *) buf)[1] =
......@@ -387,36 +446,119 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_LINK_STATE:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_LINK_STATE\n");
if (hcd->speed != HCD_USB3) {
pr_err("USB_PORT_FEAT_LINK_STATE req not "
"supported for USB 2.0 roothub\n");
goto error;
}
/*
* Since this is dummy we don't have an actual link so
* there is nothing to do for the SET_LINK_STATE cmd
*/
break;
case USB_PORT_FEAT_U1_TIMEOUT:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_U1_TIMEOUT\n");
case USB_PORT_FEAT_U2_TIMEOUT:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n");
/* TODO: add suspend/resume support! */
if (hcd->speed != HCD_USB3) {
pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not "
"supported for USB 2.0 roothub\n");
goto error;
}
break;
case USB_PORT_FEAT_SUSPEND:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
/* Applicable only for USB2.0 hub */
if (hcd->speed == HCD_USB3) {
pr_err("USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
}
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND;
break;
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_POWER\n");
if (hcd->speed == HCD_USB3)
vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
else
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_POWER;
break;
case USB_PORT_FEAT_BH_PORT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n");
/* Applicable only for USB3.0 hub */
if (hcd->speed != HCD_USB3) {
pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
"supported for USB 2.0 roothub\n");
goto error;
}
/* FALLS THROUGH */
case USB_PORT_FEAT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_RESET\n");
/* if it's already running, disconnect first */
if (vhci_hcd->port_status[rhport] & USB_PORT_STAT_ENABLE) {
vhci_hcd->port_status[rhport] &=
~(USB_PORT_STAT_ENABLE |
USB_PORT_STAT_LOW_SPEED |
USB_PORT_STAT_HIGH_SPEED);
/* FIXME test that code path! */
/* if it's already enabled, disable */
if (hcd->speed == HCD_USB3) {
vhci_hcd->port_status[rhport] = 0;
vhci_hcd->port_status[rhport] =
(USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET);
} else if (vhci_hcd->port_status[rhport] & USB_PORT_STAT_ENABLE) {
vhci_hcd->port_status[rhport] &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
}
/* 50msec reset signaling */
vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
/* FALLTHROUGH */
/* FALLS THROUGH */
default:
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
wValue);
vhci_hcd->port_status[rhport] |= (1 << wValue);
break;
if (hcd->speed == HCD_USB3) {
if ((vhci_hcd->port_status[rhport] &
USB_SS_PORT_STAT_POWER) != 0) {
vhci_hcd->port_status[rhport] |= (1 << wValue);
}
} else
if ((vhci_hcd->port_status[rhport] &
USB_PORT_STAT_POWER) != 0) {
vhci_hcd->port_status[rhport] |= (1 << wValue);
}
}
break;
case GetPortErrorCount:
usbip_dbg_vhci_rh(" GetPortErrorCount\n");
if (hcd->speed != HCD_USB3) {
pr_err("GetPortErrorCount req not "
"supported for USB 2.0 roothub\n");
goto error;
}
/* We'll always return 0 since this is a dummy hub */
*(__le32 *) buf = cpu_to_le32(0);
break;
case SetHubDepth:
usbip_dbg_vhci_rh(" SetHubDepth\n");
if (hcd->speed != HCD_USB3) {
pr_err("SetHubDepth req not supported for "
"USB 2.0 roothub\n");
goto error;
}
break;
default:
pr_err("default: no such request\n");
pr_err("default hub control req: %04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
......@@ -433,6 +575,9 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
spin_unlock_irqrestore(&vhci->lock, flags);
if ((vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0)
usb_hcd_poll_rh_status(hcd);
return retval;
}
......@@ -471,8 +616,7 @@ static void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev)
spin_unlock_irqrestore(&vdev->priv_lock, flags);
}
static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
{
struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd);
struct vhci *vhci = vhci_hcd->vhci;
......@@ -850,7 +994,6 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
pr_info("disconnect device\n");
}
static void vhci_device_reset(struct usbip_device *ud)
{
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
......@@ -926,12 +1069,22 @@ static int vhci_setup(struct usb_hcd *hcd)
{
struct vhci *vhci = *((void **)dev_get_platdata(hcd->self.controller));
hcd->self.sg_tablesize = ~0;
vhci->vhci_hcd_hs = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_hs->vhci = vhci;
hcd->speed = HCD_USB2;
hcd->self.root_hub->speed = USB_SPEED_HIGH;
if (usb_hcd_is_primary_hcd(hcd)) {
vhci->vhci_hcd_hs = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_hs->vhci = vhci;
/*
* Mark the first roothub as being USB 2.0.
* The USB 3.0 roothub will be registered later by
* vhci_hcd_probe()
*/
hcd->speed = HCD_USB2;
hcd->self.root_hub->speed = USB_SPEED_HIGH;
} else {
vhci->vhci_hcd_ss = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_ss->vhci = vhci;
hcd->speed = HCD_USB3;
hcd->self.root_hub->speed = USB_SPEED_SUPER;
}
return 0;
}
......@@ -943,7 +1096,8 @@ static int vhci_start(struct usb_hcd *hcd)
usbip_dbg_vhci_hc("enter vhci_start\n");
spin_lock_init(&vhci_hcd->vhci->lock);
if (usb_hcd_is_primary_hcd(hcd))
spin_lock_init(&vhci_hcd->vhci->lock);
/* initialize private data of usb_hcd */
......@@ -970,7 +1124,7 @@ static int vhci_start(struct usb_hcd *hcd)
}
/* vhci_hcd is now ready to be controlled through sysfs */
if (id == 0) {
if (id == 0 && usb_hcd_is_primary_hcd(hcd)) {
err = vhci_init_attr_group();
if (err) {
pr_err("init attr group\n");
......@@ -997,7 +1151,7 @@ static void vhci_stop(struct usb_hcd *hcd)
/* 1. remove the userland interface of vhci_hcd */
id = hcd_name_to_id(hcd_name(hcd));
if (id == 0) {
if (id == 0 && usb_hcd_is_primary_hcd(hcd)) {
sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group);
vhci_finish_attr_group();
}
......@@ -1058,12 +1212,30 @@ static int vhci_bus_resume(struct usb_hcd *hcd)
#define vhci_bus_resume NULL
#endif
/* Change a group of bulk endpoints to support multiple stream IDs */
static int vhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
unsigned int num_streams, gfp_t mem_flags)
{
dev_dbg(&hcd->self.root_hub->dev, "vhci_alloc_streams not implemented\n");
return 0;
}
/* Reverts a group of bulk endpoints back to not using stream IDs. */
static int vhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags)
{
dev_dbg(&hcd->self.root_hub->dev, "vhci_free_streams not implemented\n");
return 0;
}
static struct hc_driver vhci_hc_driver = {
.description = driver_name,
.product_desc = driver_desc,
.hcd_priv_size = sizeof(struct vhci_hcd),
.flags = HCD_USB2,
.flags = HCD_USB3 | HCD_SHARED,
.reset = vhci_setup,
.start = vhci_start,
......@@ -1078,12 +1250,16 @@ static struct hc_driver vhci_hc_driver = {
.hub_control = vhci_hub_control,
.bus_suspend = vhci_bus_suspend,
.bus_resume = vhci_bus_resume,
.alloc_streams = vhci_alloc_streams,
.free_streams = vhci_free_streams,
};
static int vhci_hcd_probe(struct platform_device *pdev)
{
struct vhci *vhci;
struct usb_hcd *hcd_hs;
struct usb_hcd *hcd_ss;
int ret;
usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id);
......@@ -1094,7 +1270,7 @@ static int vhci_hcd_probe(struct platform_device *pdev)
*/
hcd_hs = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd_hs) {
pr_err("create hcd failed\n");
pr_err("create primary hcd failed\n");
return -ENOMEM;
}
hcd_hs->has_tt = 1;
......@@ -1109,12 +1285,31 @@ static int vhci_hcd_probe(struct platform_device *pdev)
goto put_usb2_hcd;
}
hcd_ss = usb_create_shared_hcd(&vhci_hc_driver, &pdev->dev,
dev_name(&pdev->dev), hcd_hs);
if (!hcd_ss) {
ret = -ENOMEM;
pr_err("create shared hcd failed\n");
goto remove_usb2_hcd;
}
ret = usb_add_hcd(hcd_ss, 0, 0);
if (ret) {
pr_err("usb_add_hcd ss failed %d\n", ret);
goto put_usb3_hcd;
}
usbip_dbg_vhci_hc("bye\n");
return 0;
put_usb3_hcd:
usb_put_hcd(hcd_ss);
remove_usb2_hcd:
usb_remove_hcd(hcd_hs);
put_usb2_hcd:
usb_put_hcd(hcd_hs);
vhci->vhci_hcd_hs = NULL;
vhci->vhci_hcd_ss = NULL;
return ret;
}
......@@ -1127,10 +1322,14 @@ static int vhci_hcd_remove(struct platform_device *pdev)
* then reverses the effects of usb_add_hcd(),
* invoking the HCD's stop() methods.
*/
usb_remove_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_ss));
usb_put_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_ss));
usb_remove_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_hs));
usb_put_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_hs));
vhci->vhci_hcd_hs = NULL;
vhci->vhci_hcd_ss = NULL;
return 0;
}
......@@ -1142,7 +1341,7 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
{
struct usb_hcd *hcd;
struct vhci *vhci;
int rhport = 0;
int rhport;
int connected = 0;
int ret = 0;
unsigned long flags;
......@@ -1161,6 +1360,10 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
if (vhci->vhci_hcd_hs->port_status[rhport] &
USB_PORT_STAT_CONNECTION)
connected += 1;
if (vhci->vhci_hcd_ss->port_status[rhport] &
USB_PORT_STAT_CONNECTION)
connected += 1;
}
spin_unlock_irqrestore(&vhci->lock, flags);
......
......@@ -29,6 +29,42 @@
/* TODO: refine locking ?*/
/*
* output example:
* hub port sta spd dev socket local_busid
* hs 0000 004 000 00000000 c5a7bb80 1-2.3
* ................................................
* ss 0008 004 000 00000000 d8cee980 2-3.4
* ................................................
*
* IP address can be retrieved from a socket pointer address by looking
* up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
* port number and its peer IP address.
*/
static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vdev)
{
if (hub == HUB_SPEED_HIGH)
*out += sprintf(*out, "hs %04u %03u ",
port, vdev->ud.status);
else /* hub == HUB_SPEED_SUPER */
*out += sprintf(*out, "ss %04u %03u ",
port, vdev->ud.status);
if (vdev->ud.status == VDEV_ST_USED) {
*out += sprintf(*out, "%03u %08x ",
vdev->speed, vdev->devid);
*out += sprintf(*out, "%16p %s",
vdev->ud.tcp_socket,
dev_name(&vdev->udev->dev));
} else {
*out += sprintf(*out, "000 00000000 ");
*out += sprintf(*out, "0000000000000000 0-0");
}
*out += sprintf(*out, "\n");
}
/* Sysfs entry to show port status */
static ssize_t status_show_vhci(int pdev_nr, char *out)
{
......@@ -51,37 +87,21 @@ static ssize_t status_show_vhci(int pdev_nr, char *out)
spin_lock_irqsave(&vhci->lock, flags);
/*
* output example:
* port sta spd dev socket local_busid
* 0000 004 000 00000000 c5a7bb80 1-2.3
* 0001 004 000 00000000 d8cee980 2-3.4
*
* IP address can be retrieved from a socket pointer address by looking
* up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
* port number and its peer IP address.
*/
for (i = 0; i < VHCI_HC_PORTS; i++) {
struct vhci_device *vdev = &vhci_hcd->vdev[i];
struct vhci_device *vdev = &vhci->vhci_hcd_hs->vdev[i];
spin_lock(&vdev->ud.lock);
out += sprintf(out, "%04u %03u ",
(pdev_nr * VHCI_HC_PORTS) + i,
vdev->ud.status);
if (vdev->ud.status == VDEV_ST_USED) {
out += sprintf(out, "%03u %08x ",
vdev->speed, vdev->devid);
out += sprintf(out, "%16p %s",
vdev->ud.tcp_socket,
dev_name(&vdev->udev->dev));
} else {
out += sprintf(out, "000 00000000 ");
out += sprintf(out, "0000000000000000 0-0");
}
port_show_vhci(&out, HUB_SPEED_HIGH,
pdev_nr * VHCI_HC_PORTS * 2 + i, vdev);
spin_unlock(&vdev->ud.lock);
}
out += sprintf(out, "\n");
for (i = 0; i < VHCI_HC_PORTS; i++) {
struct vhci_device *vdev = &vhci->vhci_hcd_ss->vdev[i];
spin_lock(&vdev->ud.lock);
port_show_vhci(&out, HUB_SPEED_SUPER,
pdev_nr * VHCI_HC_PORTS * 2 + VHCI_HC_PORTS + i, vdev);
spin_unlock(&vdev->ud.lock);
}
......@@ -96,8 +116,16 @@ static ssize_t status_show_not_ready(int pdev_nr, char *out)
int i = 0;
for (i = 0; i < VHCI_HC_PORTS; i++) {
out += sprintf(out, "%04u %03u ",
(pdev_nr * VHCI_HC_PORTS) + i,
out += sprintf(out, "hs %04u %03u ",
(pdev_nr * VHCI_HC_PORTS * 2) + i,
VDEV_ST_NOTASSIGNED);
out += sprintf(out, "000 00000000 0000000000000000 0-0");
out += sprintf(out, "\n");
}
for (i = 0; i < VHCI_HC_PORTS; i++) {
out += sprintf(out, "ss %04u %03u ",
(pdev_nr * VHCI_HC_PORTS * 2) + VHCI_HC_PORTS + i,
VDEV_ST_NOTASSIGNED);
out += sprintf(out, "000 00000000 0000000000000000 0-0");
out += sprintf(out, "\n");
......@@ -129,7 +157,7 @@ static ssize_t status_show(struct device *dev,
int pdev_nr;
out += sprintf(out,
"port sta spd dev socket local_busid\n");
"hub port sta spd dev socket local_busid\n");
pdev_nr = status_name_to_id(attr->attr.name);
if (pdev_nr < 0)
......@@ -145,7 +173,10 @@ static ssize_t nports_show(struct device *dev, struct device_attribute *attr,
{
char *s = out;
out += sprintf(out, "%d\n", VHCI_HC_PORTS * vhci_num_controllers);
/*
* Half the ports are for SPEED_HIGH and half for SPEED_SUPER, thus the * 2.
*/
out += sprintf(out, "%d\n", VHCI_HC_PORTS * vhci_num_controllers * 2);
return out - s;
}
static DEVICE_ATTR_RO(nports);
......@@ -200,6 +231,7 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
{
__u32 port = 0, pdev_nr = 0, rhport = 0;
struct usb_hcd *hcd;
struct vhci_hcd *vhci_hcd;
int ret;
if (kstrtoint(buf, 10, &port) < 0)
......@@ -217,7 +249,14 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
return -EAGAIN;
}
ret = vhci_port_disconnect(hcd_to_vhci_hcd(hcd), rhport);
usbip_dbg_vhci_sysfs("rhport %d\n", rhport);
if ((port / VHCI_HC_PORTS) % 2)
vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_ss;
else
vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_hs;
ret = vhci_port_disconnect(vhci_hcd, rhport);
if (ret < 0)
return -EINVAL;
......@@ -301,7 +340,11 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
vdev = &vhci_hcd->vdev[rhport];
if (speed == USB_SPEED_SUPER)
vdev = &vhci->vhci_hcd_ss->vdev[rhport];
else
vdev = &vhci->vhci_hcd_hs->vdev[rhport];
/* Extract socket from fd. */
socket = sockfd_lookup(sockfd, &err);
......
......@@ -52,9 +52,10 @@ static int parse_status(const char *value)
unsigned long socket;
char lbusid[SYSFS_BUS_ID_SIZE];
struct usbip_imported_device *idev;
char hub[3];
ret = sscanf(c, "%d %d %d %x %lx %31s\n",
&port, &status, &speed,
ret = sscanf(c, "%2s %d %d %d %x %lx %31s\n",
hub, &port, &status, &speed,
&devid, &socket, lbusid);
if (ret < 5) {
......@@ -62,15 +63,19 @@ static int parse_status(const char *value)
BUG();
}
dbg("port %d status %d speed %d devid %x",
port, status, speed, devid);
dbg("hub %s port %d status %d speed %d devid %x",
hub, port, status, speed, devid);
dbg("socket %lx lbusid %s", socket, lbusid);
/* if a device is connected, look at it */
idev = &vhci_driver->idev[port];
memset(idev, 0, sizeof(*idev));
if (strncmp("hs", hub, 2) == 0)
idev->hub = HUB_SPEED_HIGH;
else /* strncmp("ss", hub, 2) == 0 */
idev->hub = HUB_SPEED_SUPER;
idev->port = port;
idev->status = status;
......@@ -320,11 +325,15 @@ int usbip_vhci_refresh_device_list(void)
}
int usbip_vhci_get_free_port(void)
int usbip_vhci_get_free_port(uint32_t speed)
{
for (int i = 0; i < vhci_driver->nports; i++) {
if (speed == USB_SPEED_SUPER &&
vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
continue;
if (vhci_driver->idev[i].status == VDEV_ST_NULL)
return i;
return vhci_driver->idev[i].port;
}
return -1;
......
......@@ -14,7 +14,13 @@
#define USBIP_VHCI_DEVICE_NAME "vhci_hcd.0"
#define MAXNPORT 128
enum hub_speed {
HUB_SPEED_HIGH = 0,
HUB_SPEED_SUPER,
};
struct usbip_imported_device {
enum hub_speed hub;
uint8_t port;
uint32_t status;
......@@ -46,7 +52,7 @@ void usbip_vhci_driver_close(void);
int usbip_vhci_refresh_device_list(void);
int usbip_vhci_get_free_port(void);
int usbip_vhci_get_free_port(uint32_t speed);
int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
uint32_t speed);
......
......@@ -94,6 +94,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
{
int rc;
int port;
uint32_t speed = udev->speed;
rc = usbip_vhci_driver_open();
if (rc < 0) {
......@@ -101,7 +102,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
return -1;
}
port = usbip_vhci_get_free_port();
port = usbip_vhci_get_free_port(speed);
if (port < 0) {
err("no free port");
usbip_vhci_driver_close();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment