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 { ...@@ -72,6 +72,11 @@ struct vhci_unlink {
unsigned long unlink_seqnum; 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 */ /* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
#ifdef CONFIG_USBIP_VHCI_HC_PORTS #ifdef CONFIG_USBIP_VHCI_HC_PORTS
#define VHCI_HC_PORTS 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) ...@@ -140,7 +145,7 @@ static inline __u32 port_to_rhport(__u32 port)
static inline int port_to_pdev_nr(__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) static inline struct vhci_hcd *hcd_to_vhci_hcd(struct usb_hcd *hcd)
......
This diff is collapsed.
...@@ -29,6 +29,42 @@ ...@@ -29,6 +29,42 @@
/* TODO: refine locking ?*/ /* 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 */ /* Sysfs entry to show port status */
static ssize_t status_show_vhci(int pdev_nr, char *out) 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) ...@@ -51,37 +87,21 @@ static ssize_t status_show_vhci(int pdev_nr, char *out)
spin_lock_irqsave(&vhci->lock, flags); 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++) { 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); spin_lock(&vdev->ud.lock);
out += sprintf(out, "%04u %03u ", port_show_vhci(&out, HUB_SPEED_HIGH,
(pdev_nr * VHCI_HC_PORTS) + i, pdev_nr * VHCI_HC_PORTS * 2 + i, vdev);
vdev->ud.status); spin_unlock(&vdev->ud.lock);
}
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"); 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); spin_unlock(&vdev->ud.lock);
} }
...@@ -96,8 +116,16 @@ static ssize_t status_show_not_ready(int pdev_nr, char *out) ...@@ -96,8 +116,16 @@ static ssize_t status_show_not_ready(int pdev_nr, char *out)
int i = 0; int i = 0;
for (i = 0; i < VHCI_HC_PORTS; i++) { for (i = 0; i < VHCI_HC_PORTS; i++) {
out += sprintf(out, "%04u %03u ", out += sprintf(out, "hs %04u %03u ",
(pdev_nr * VHCI_HC_PORTS) + i, (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); VDEV_ST_NOTASSIGNED);
out += sprintf(out, "000 00000000 0000000000000000 0-0"); out += sprintf(out, "000 00000000 0000000000000000 0-0");
out += sprintf(out, "\n"); out += sprintf(out, "\n");
...@@ -129,7 +157,7 @@ static ssize_t status_show(struct device *dev, ...@@ -129,7 +157,7 @@ static ssize_t status_show(struct device *dev,
int pdev_nr; int pdev_nr;
out += sprintf(out, 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); pdev_nr = status_name_to_id(attr->attr.name);
if (pdev_nr < 0) if (pdev_nr < 0)
...@@ -145,7 +173,10 @@ static ssize_t nports_show(struct device *dev, struct device_attribute *attr, ...@@ -145,7 +173,10 @@ static ssize_t nports_show(struct device *dev, struct device_attribute *attr,
{ {
char *s = out; 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; return out - s;
} }
static DEVICE_ATTR_RO(nports); static DEVICE_ATTR_RO(nports);
...@@ -200,6 +231,7 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr, ...@@ -200,6 +231,7 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
{ {
__u32 port = 0, pdev_nr = 0, rhport = 0; __u32 port = 0, pdev_nr = 0, rhport = 0;
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct vhci_hcd *vhci_hcd;
int ret; int ret;
if (kstrtoint(buf, 10, &port) < 0) if (kstrtoint(buf, 10, &port) < 0)
...@@ -217,7 +249,14 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr, ...@@ -217,7 +249,14 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
return -EAGAIN; 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) if (ret < 0)
return -EINVAL; return -EINVAL;
...@@ -301,7 +340,11 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, ...@@ -301,7 +340,11 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
vhci_hcd = hcd_to_vhci_hcd(hcd); vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci; 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. */ /* Extract socket from fd. */
socket = sockfd_lookup(sockfd, &err); socket = sockfd_lookup(sockfd, &err);
......
...@@ -52,9 +52,10 @@ static int parse_status(const char *value) ...@@ -52,9 +52,10 @@ static int parse_status(const char *value)
unsigned long socket; unsigned long socket;
char lbusid[SYSFS_BUS_ID_SIZE]; char lbusid[SYSFS_BUS_ID_SIZE];
struct usbip_imported_device *idev; struct usbip_imported_device *idev;
char hub[3];
ret = sscanf(c, "%d %d %d %x %lx %31s\n", ret = sscanf(c, "%2s %d %d %d %x %lx %31s\n",
&port, &status, &speed, hub, &port, &status, &speed,
&devid, &socket, lbusid); &devid, &socket, lbusid);
if (ret < 5) { if (ret < 5) {
...@@ -62,15 +63,19 @@ static int parse_status(const char *value) ...@@ -62,15 +63,19 @@ static int parse_status(const char *value)
BUG(); BUG();
} }
dbg("port %d status %d speed %d devid %x", dbg("hub %s port %d status %d speed %d devid %x",
port, status, speed, devid); hub, port, status, speed, devid);
dbg("socket %lx lbusid %s", socket, lbusid); dbg("socket %lx lbusid %s", socket, lbusid);
/* if a device is connected, look at it */ /* if a device is connected, look at it */
idev = &vhci_driver->idev[port]; idev = &vhci_driver->idev[port];
memset(idev, 0, sizeof(*idev)); 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->port = port;
idev->status = status; idev->status = status;
...@@ -320,11 +325,15 @@ int usbip_vhci_refresh_device_list(void) ...@@ -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++) { 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) if (vhci_driver->idev[i].status == VDEV_ST_NULL)
return i; return vhci_driver->idev[i].port;
} }
return -1; return -1;
......
...@@ -14,7 +14,13 @@ ...@@ -14,7 +14,13 @@
#define USBIP_VHCI_DEVICE_NAME "vhci_hcd.0" #define USBIP_VHCI_DEVICE_NAME "vhci_hcd.0"
#define MAXNPORT 128 #define MAXNPORT 128
enum hub_speed {
HUB_SPEED_HIGH = 0,
HUB_SPEED_SUPER,
};
struct usbip_imported_device { struct usbip_imported_device {
enum hub_speed hub;
uint8_t port; uint8_t port;
uint32_t status; uint32_t status;
...@@ -46,7 +52,7 @@ void usbip_vhci_driver_close(void); ...@@ -46,7 +52,7 @@ void usbip_vhci_driver_close(void);
int usbip_vhci_refresh_device_list(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, int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
uint32_t speed); uint32_t speed);
......
...@@ -94,6 +94,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev) ...@@ -94,6 +94,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
{ {
int rc; int rc;
int port; int port;
uint32_t speed = udev->speed;
rc = usbip_vhci_driver_open(); rc = usbip_vhci_driver_open();
if (rc < 0) { if (rc < 0) {
...@@ -101,7 +102,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev) ...@@ -101,7 +102,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
return -1; return -1;
} }
port = usbip_vhci_get_free_port(); port = usbip_vhci_get_free_port(speed);
if (port < 0) { if (port < 0) {
err("no free port"); err("no free port");
usbip_vhci_driver_close(); 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