Commit 1cd8fd28 authored by Tatyana Brokhman's avatar Tatyana Brokhman Committed by Greg Kroah-Hartman

usb: gadget: dummy_hcd: add SuperSpeed support

This patch adds SS support to the dummy hcd module.
It may be used to test SS device when no (SS) HW is
available.

USB 3.0 hub includes 2 hubs - one HS and one SS. This
patch adds support for a SS root hub in the dummy_hcd
module.

A new module parameter was added: is_super_speed. When
set to true, a SS root hub will also be registered and
the connected device will be enumerated over the SS
root hub. The default of this parameter is false.
Signed-off-by: default avatarTatyana Brokhman <tlinder@codeaurora.org>

[ balbi@ti.com : slight change to commit log
		 fixed one coding style issue ]
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent cdfcbd2c
......@@ -631,6 +631,7 @@ config USB_GADGET_DUMMY_HCD
boolean "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
select USB_GADGET_DUALSPEED
select USB_GADGET_SUPERSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
......
......@@ -70,6 +70,15 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
struct dummy_hcd_module_parameters {
bool is_super_speed;
};
static struct dummy_hcd_module_parameters mod_data = {
.is_super_speed = false
};
module_param_named(is_super_speed, mod_data.is_super_speed, bool, S_IRUGO);
MODULE_PARM_DESC(is_super_speed, "true to simulate SuperSpeed connection");
/*-------------------------------------------------------------------------*/
/* gadget side driver data structres */
......@@ -188,6 +197,7 @@ struct dummy {
* MASTER/HOST side support
*/
struct dummy_hcd *hs_hcd;
struct dummy_hcd *ss_hcd;
};
static inline struct dummy_hcd *hcd_to_dummy_hcd(struct usb_hcd *hcd)
......@@ -218,6 +228,9 @@ static inline struct dummy *ep_to_dummy (struct dummy_ep *ep)
static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget)
{
struct dummy *dum = container_of(gadget, struct dummy, gadget);
if (dum->gadget.speed == USB_SPEED_SUPER)
return dum->ss_hcd;
else
return dum->hs_hcd;
}
......@@ -266,60 +279,117 @@ stop_activity (struct dummy *dum)
/* driver now does any non-usb quiescing necessary */
}
/* caller must hold lock */
static void set_link_state(struct dummy_hcd *dum_hcd)
/**
* set_link_state_by_speed() - Sets the current state of the link according to
* the hcd speed
* @dum_hcd: pointer to the dummy_hcd structure to update the link state for
*
* This function updates the port_status according to the link state and the
* speed of the hcd.
*/
static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
{
struct dummy *dum = dum_hcd->dum;
dum_hcd->active = 0;
if ((dum_hcd->port_status & USB_PORT_STAT_POWER) == 0)
if (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3) {
if ((dum_hcd->port_status & USB_SS_PORT_STAT_POWER) == 0) {
dum_hcd->port_status = 0;
} else if (!dum->pullup || dum->udc_suspended) {
/* UDC suspend must cause a disconnect */
dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_ENABLE);
if ((dum_hcd->old_status &
USB_PORT_STAT_CONNECTION) != 0)
dum_hcd->port_status |=
(USB_PORT_STAT_C_CONNECTION << 16);
} else {
/* device is connected and not suspended */
dum_hcd->port_status |= (USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_SPEED_5GBPS) ;
if ((dum_hcd->old_status &
USB_PORT_STAT_CONNECTION) == 0)
dum_hcd->port_status |=
(USB_PORT_STAT_C_CONNECTION << 16);
if ((dum_hcd->port_status &
USB_PORT_STAT_ENABLE) == 1 &&
(dum_hcd->port_status &
USB_SS_PORT_LS_U0) == 1 &&
dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
dum_hcd->active = 1;
}
} else {
if ((dum_hcd->port_status & USB_PORT_STAT_POWER) == 0) {
dum_hcd->port_status = 0;
} else if (!dum->pullup || dum->udc_suspended) {
/* UDC suspend must cause a disconnect */
else if (!dum->pullup || dum->udc_suspended) {
dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_ENABLE |
USB_PORT_STAT_LOW_SPEED |
USB_PORT_STAT_HIGH_SPEED |
USB_PORT_STAT_SUSPEND);
if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0)
if ((dum_hcd->old_status &
USB_PORT_STAT_CONNECTION) != 0)
dum_hcd->port_status |=
(USB_PORT_STAT_C_CONNECTION << 16);
} else {
dum_hcd->port_status |= USB_PORT_STAT_CONNECTION;
if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) == 0)
if ((dum_hcd->old_status &
USB_PORT_STAT_CONNECTION) == 0)
dum_hcd->port_status |=
(USB_PORT_STAT_C_CONNECTION << 16);
if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0)
dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND;
else if ((dum_hcd->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
else if ((dum_hcd->port_status &
USB_PORT_STAT_SUSPEND) == 0 &&
dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
dum_hcd->active = 1;
}
}
}
/* caller must hold lock */
static void set_link_state(struct dummy_hcd *dum_hcd)
{
struct dummy *dum = dum_hcd->dum;
dum_hcd->active = 0;
if (dum->pullup)
if ((dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 &&
dum->gadget.speed != USB_SPEED_SUPER) ||
(dummy_hcd_to_hcd(dum_hcd)->speed != HCD_USB3 &&
dum->gadget.speed == USB_SPEED_SUPER))
return;
set_link_state_by_speed(dum_hcd);
if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
dum_hcd->active)
dum_hcd->resuming = 0;
/* if !connected or reset */
if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
/*
* We're connected and not reset (reset occurred now),
* and driver attached - disconnect!
*/
if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
(dum_hcd->old_status & USB_PORT_STAT_RESET) == 0 &&
dum->driver) {
stop_activity (dum);
spin_unlock (&dum->lock);
dum->driver->disconnect (&dum->gadget);
spin_lock (&dum->lock);
stop_activity(dum);
spin_unlock(&dum->lock);
dum->driver->disconnect(&dum->gadget);
spin_lock(&dum->lock);
}
} else if (dum_hcd->active != dum_hcd->old_active) {
if (dum_hcd->old_active && dum->driver->suspend) {
spin_unlock (&dum->lock);
dum->driver->suspend (&dum->gadget);
spin_lock (&dum->lock);
spin_unlock(&dum->lock);
dum->driver->suspend(&dum->gadget);
spin_lock(&dum->lock);
} else if (!dum_hcd->old_active && dum->driver->resume) {
spin_unlock (&dum->lock);
dum->driver->resume (&dum->gadget);
spin_lock (&dum->lock);
spin_unlock(&dum->lock);
dum->driver->resume(&dum->gadget);
spin_lock(&dum->lock);
}
}
......@@ -355,6 +425,9 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
dum = ep_to_dummy (ep);
if (!dum->driver)
return -ESHUTDOWN;
if (dum->gadget.speed == USB_SPEED_SUPER)
dum_hcd = dum->ss_hcd;
else
dum_hcd = dum->hs_hcd;
if (!is_enabled(dum_hcd))
return -ESHUTDOWN;
......@@ -362,6 +435,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/*
* For HS/FS devices only bits 0..10 of the wMaxPacketSize represent the
* maximum packet size.
* For SS devices the wMaxPacketSize is limited by 1024.
*/
max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
......@@ -381,6 +455,10 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto done;
}
switch (dum->gadget.speed) {
case USB_SPEED_SUPER:
if (max == 1024)
break;
goto done;
case USB_SPEED_HIGH:
if (max == 512)
break;
......@@ -399,6 +477,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto done;
/* real hardware might not handle all packet sizes */
switch (dum->gadget.speed) {
case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
if (max <= 1024)
break;
......@@ -419,6 +498,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto done;
/* real hardware might not handle all packet sizes */
switch (dum->gadget.speed) {
case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
if (max <= 1024)
break;
......@@ -547,6 +627,9 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
return -EINVAL;
dum = ep_to_dummy (ep);
if (dum->gadget.speed == USB_SPEED_SUPER)
dum_hcd = dum->ss_hcd;
else
dum_hcd = dum->hs_hcd;
if (!dum->driver || !is_enabled(dum_hcd))
return -ESHUTDOWN;
......@@ -733,9 +816,12 @@ static int dummy_pullup (struct usb_gadget *_gadget, int value)
dum = gadget_to_dummy_hcd(_gadget)->dum;
spin_lock_irqsave (&dum->lock, flags);
dum->pullup = (value != 0);
set_link_state(dum->hs_hcd);
set_link_state((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd));
spin_unlock_irqrestore (&dum->lock, flags);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum->hs_hcd));
usb_hcd_poll_rh_status((dum->gadget.speed == USB_SPEED_SUPER ?
dummy_hcd_to_hcd(dum->ss_hcd) :
dummy_hcd_to_hcd(dum->hs_hcd)));
return 0;
}
......@@ -821,7 +907,20 @@ static int dummy_udc_start(struct usb_gadget_driver *driver,
}
dum->gadget.ep0 = &dum->ep [0].ep;
dum->gadget.speed = min((u8)driver->speed, (u8)USB_SPEED_HIGH) ;
if (mod_data.is_super_speed)
dum->gadget.speed = driver->speed;
else
dum->gadget.speed = min((u8)USB_SPEED_HIGH, (u8)driver->speed);
if (dum->gadget.speed < driver->speed)
dev_dbg(udc_dev(dum), "This device can perform faster if"
" you connect it to a "
"SupeSpeed port...\n");
if (dum->gadget.speed == USB_SPEED_SUPER) {
for (i = 0; i < DUMMY_ENDPOINTS; i++)
dum->ep[i].ep.max_streams = 0x10;
dum->ep[0].ep.maxpacket = 9;
} else
dum->ep[0].ep.maxpacket = 64;
list_del_init (&dum->ep [0].ep.ep_list);
INIT_LIST_HEAD(&dum->fifo_req.queue);
......@@ -841,12 +940,21 @@ static int dummy_udc_start(struct usb_gadget_driver *driver,
/* khubd will enumerate this in a while */
spin_lock_irq (&dum->lock);
dum->pullup = 1;
if (dum->gadget.speed == USB_SPEED_SUPER) {
dum->gadget.is_otg =
(dummy_hcd_to_hcd(dum->ss_hcd)->self.otg_port != 0);
set_link_state(dum->ss_hcd);
} else {
dum->gadget.is_otg =
(dummy_hcd_to_hcd(dum->hs_hcd)->self.otg_port != 0);
set_link_state(dum->hs_hcd);
}
spin_unlock_irq (&dum->lock);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum->hs_hcd));
usb_hcd_poll_rh_status((dum->gadget.speed == USB_SPEED_SUPER ?
dummy_hcd_to_hcd(dum->ss_hcd) :
dummy_hcd_to_hcd(dum->hs_hcd)));
return 0;
}
......@@ -865,18 +973,21 @@ static int dummy_udc_stop(struct usb_gadget_driver *driver)
spin_lock_irqsave (&dum->lock, flags);
dum->pullup = 0;
set_link_state(dum->hs_hcd);
set_link_state((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd));
spin_unlock_irqrestore (&dum->lock, flags);
driver->unbind (&dum->gadget);
dum->gadget.dev.driver = NULL;
dum->driver = NULL;
spin_lock_irqsave (&dum->lock, flags);
dum->pullup = 0;
set_link_state(dum->hs_hcd);
set_link_state((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd));
spin_unlock_irqrestore (&dum->lock, flags);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum->hs_hcd));
usb_hcd_poll_rh_status((dum->gadget.speed == USB_SPEED_SUPER ?
dummy_hcd_to_hcd(dum->ss_hcd) :
dummy_hcd_to_hcd(dum->hs_hcd)));
return 0;
}
......@@ -953,10 +1064,13 @@ static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state)
dev_dbg (&pdev->dev, "%s\n", __func__);
spin_lock_irq (&dum->lock);
dum->udc_suspended = 1;
set_link_state(dum->hs_hcd);
set_link_state((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd));
spin_unlock_irq (&dum->lock);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum->hs_hcd));
usb_hcd_poll_rh_status((dum->gadget.speed == USB_SPEED_SUPER ?
dummy_hcd_to_hcd(dum->ss_hcd) :
dummy_hcd_to_hcd(dum->hs_hcd)));
return 0;
}
......@@ -967,10 +1081,13 @@ static int dummy_udc_resume (struct platform_device *pdev)
dev_dbg (&pdev->dev, "%s\n", __func__);
spin_lock_irq (&dum->lock);
dum->udc_suspended = 0;
set_link_state(dum->hs_hcd);
set_link_state((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd));
spin_unlock_irq (&dum->lock);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum->hs_hcd));
usb_hcd_poll_rh_status((dum->gadget.speed == USB_SPEED_SUPER ?
dummy_hcd_to_hcd(dum->ss_hcd) :
dummy_hcd_to_hcd(dum->hs_hcd)));
return 0;
}
......@@ -1198,6 +1315,21 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
}
if (dum->gadget.speed == USB_SPEED_SUPER) {
switch (ep->desc->bmAttributes & 0x03) {
case USB_ENDPOINT_XFER_ISOC:
/* Sec. 4.4.8.2 USB3.0 Spec */
limit = 3 * 16 * 1024 * 8;
break;
case USB_ENDPOINT_XFER_INT:
/* Sec. 4.4.7.2 USB3.0 Spec */
limit = 3 * 1024 * 8;
break;
case USB_ENDPOINT_XFER_BULK:
default:
break;
}
}
return limit;
}
......@@ -1210,7 +1342,8 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
{
int i;
if (!is_active(dum->hs_hcd))
if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd)))
return NULL;
if ((address & ~USB_DIR_IN) == 0)
return &dum->ep [0];
......@@ -1284,6 +1417,27 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
case USB_DEVICE_A_ALT_HNP_SUPPORT:
dum->gadget.a_alt_hnp_support = 1;
break;
case USB_DEVICE_U1_ENABLE:
if (dummy_hcd_to_hcd(dum_hcd)->speed ==
HCD_USB3)
w_value = USB_DEV_STAT_U1_ENABLED;
else
ret_val = -EOPNOTSUPP;
break;
case USB_DEVICE_U2_ENABLE:
if (dummy_hcd_to_hcd(dum_hcd)->speed ==
HCD_USB3)
w_value = USB_DEV_STAT_U2_ENABLED;
else
ret_val = -EOPNOTSUPP;
break;
case USB_DEVICE_LTM_ENABLE:
if (dummy_hcd_to_hcd(dum_hcd)->speed ==
HCD_USB3)
w_value = USB_DEV_STAT_LTM_ENABLED;
else
ret_val = -EOPNOTSUPP;
break;
default:
ret_val = -EOPNOTSUPP;
}
......@@ -1310,6 +1464,27 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
case USB_DEVICE_REMOTE_WAKEUP:
w_value = USB_DEVICE_REMOTE_WAKEUP;
break;
case USB_DEVICE_U1_ENABLE:
if (dummy_hcd_to_hcd(dum_hcd)->speed ==
HCD_USB3)
w_value = USB_DEV_STAT_U1_ENABLED;
else
ret_val = -EOPNOTSUPP;
break;
case USB_DEVICE_U2_ENABLE:
if (dummy_hcd_to_hcd(dum_hcd)->speed ==
HCD_USB3)
w_value = USB_DEV_STAT_U2_ENABLED;
else
ret_val = -EOPNOTSUPP;
break;
case USB_DEVICE_LTM_ENABLE:
if (dummy_hcd_to_hcd(dum_hcd)->speed ==
HCD_USB3)
w_value = USB_DEV_STAT_LTM_ENABLED;
else
ret_val = -EOPNOTSUPP;
break;
default:
ret_val = -EOPNOTSUPP;
break;
......@@ -1391,6 +1566,10 @@ static void dummy_timer(unsigned long _dum_hcd)
case USB_SPEED_HIGH:
total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
break;
case USB_SPEED_SUPER:
/* Bus speed is 500000 bytes/ms, so use a little less */
total = 490000;
break;
default:
dev_err(dummy_dev(dum_hcd), "bogus device speed\n");
return;
......@@ -1624,6 +1803,18 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
return retval;
}
static inline void
ss_hub_descriptor(struct usb_hub_descriptor *desc)
{
memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x2a;
desc->bDescLength = 12;
desc->wHubCharacteristics = cpu_to_le16(0x0001);
desc->bNbrPorts = 1;
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)
{
......@@ -1660,6 +1851,12 @@ static int dummy_hub_control (
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed == HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
}
if (dum_hcd->port_status & USB_PORT_STAT_SUSPEND) {
/* 20msec resume signaling */
dum_hcd->resuming = 1;
......@@ -1668,8 +1865,15 @@ static int dummy_hub_control (
}
break;
case USB_PORT_FEAT_POWER:
if (hcd->speed == HCD_USB3) {
if (dum_hcd->port_status & USB_PORT_STAT_POWER)
dev_dbg(dummy_dev(dum_hcd), "power-off\n");
dev_dbg(dummy_dev(dum_hcd),
"power-off\n");
} else
if (dum_hcd->port_status &
USB_SS_PORT_STAT_POWER)
dev_dbg(dummy_dev(dum_hcd),
"power-off\n");
/* FALLS THROUGH */
default:
dum_hcd->port_status &= ~(1 << wValue);
......@@ -1677,6 +1881,17 @@ static int dummy_hub_control (
}
break;
case GetHubDescriptor:
if (hcd->speed == HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
dev_dbg(dummy_dev(dum_hcd),
"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 GetHubStatus:
......@@ -1700,6 +1915,8 @@ static int dummy_hub_control (
dum_hcd->port_status &= ~USB_PORT_STAT_RESET;
if (dum_hcd->dum->pullup) {
dum_hcd->port_status |= USB_PORT_STAT_ENABLE;
if (hcd->speed < HCD_USB3) {
switch (dum_hcd->dum->gadget.speed) {
case USB_SPEED_HIGH:
dum_hcd->port_status |=
......@@ -1718,6 +1935,7 @@ static int dummy_hub_control (
}
}
}
}
set_link_state(dum_hcd);
((__le16 *) buf)[0] = cpu_to_le16 (dum_hcd->port_status);
((__le16 *) buf)[1] = cpu_to_le16 (dum_hcd->port_status >> 16);
......@@ -1727,7 +1945,36 @@ static int dummy_hub_control (
break;
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_LINK_STATE:
if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"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:
case USB_PORT_FEAT_U2_TIMEOUT:
/* TODO: add suspend/resume support! */
if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"USB_PORT_FEAT_U1/2_TIMEOUT req not "
"supported for USB 2.0 roothub\n");
goto error;
}
break;
case USB_PORT_FEAT_SUSPEND:
/* Applicable only for USB2.0 hub */
if (hcd->speed == HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
}
if (dum_hcd->active) {
dum_hcd->port_status |= USB_PORT_STAT_SUSPEND;
......@@ -1742,11 +1989,30 @@ static int dummy_hub_control (
}
break;
case USB_PORT_FEAT_POWER:
if (hcd->speed == HCD_USB3)
dum_hcd->port_status |= USB_SS_PORT_STAT_POWER;
else
dum_hcd->port_status |= USB_PORT_STAT_POWER;
set_link_state(dum_hcd);
break;
case USB_PORT_FEAT_BH_PORT_RESET:
/* Applicable only for USB3.0 hub */
if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"USB_PORT_FEAT_BH_PORT_RESET req not "
"supported for USB 2.0 roothub\n");
goto error;
}
/* FALLS THROUGH */
case USB_PORT_FEAT_RESET:
/* if it's already enabled, disable */
if (hcd->speed == HCD_USB3) {
dum_hcd->port_status = 0;
dum_hcd->port_status =
(USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET);
} else
dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
......@@ -1756,9 +2022,20 @@ static int dummy_hub_control (
*/
dum_hcd->dum->devstatus &=
(1 << USB_DEVICE_SELF_POWERED);
/*
* FIXME USB3.0: what is the correct reset signaling
* interval? Is it still 50msec as for HS?
*/
dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
/* FALLS THROUGH */
default:
if (hcd->speed == HCD_USB3) {
if ((dum_hcd->port_status &
USB_SS_PORT_STAT_POWER) != 0) {
dum_hcd->port_status |= (1 << wValue);
set_link_state(dum_hcd);
}
} else
if ((dum_hcd->port_status &
USB_PORT_STAT_POWER) != 0) {
dum_hcd->port_status |= (1 << wValue);
......@@ -1766,11 +2043,29 @@ static int dummy_hub_control (
}
}
break;
case GetPortErrorCount:
if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"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:
if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd),
"SetHubDepth req not supported for "
"USB 2.0 roothub\n");
goto error;
}
break;
default:
dev_dbg(dummy_dev(dum_hcd),
"hub control req%04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
......@@ -1837,6 +2132,9 @@ show_urb (char *buf, size_t size, struct urb *urb)
case USB_SPEED_HIGH:
s = "hs";
break;
case USB_SPEED_SUPER:
s = "ss";
break;
default:
s = "?";
break;
......@@ -1883,6 +2181,25 @@ show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
init_timer(&dum_hcd->timer);
dum_hcd->timer.function = dummy_timer;
dum_hcd->timer.data = (unsigned long)dum_hcd;
dum_hcd->rh_state = DUMMY_RH_RUNNING;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET;
dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING;
dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1;
#ifdef CONFIG_USB_OTG
dummy_hcd_to_hcd(dum_hcd)->self.otg_port = 1;
#endif
return 0;
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs);
}
static int dummy_start(struct usb_hcd *hcd)
{
struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
......@@ -1892,6 +2209,9 @@ static int dummy_start(struct usb_hcd *hcd)
* talk to one device (the slave side). Also appears in sysfs,
* just like more familiar pci-based HCDs.
*/
if (!usb_hcd_is_primary_hcd(hcd))
return dummy_start_ss(dum_hcd);
spin_lock_init(&dum_hcd->dum->lock);
init_timer(&dum_hcd->timer);
dum_hcd->timer.function = dummy_timer;
......@@ -1934,19 +2254,52 @@ static int dummy_setup(struct usb_hcd *hcd)
if (usb_hcd_is_primary_hcd(hcd)) {
the_controller.hs_hcd = hcd_to_dummy_hcd(hcd);
the_controller.hs_hcd->dum = &the_controller;
/* Mark the first roothub as being USB 2.0. */
/*
* Mark the first roothub as being USB 2.0.
* The USB 3.0 roothub will be registered later by
* dummy_hcd_probe()
*/
hcd->speed = HCD_USB2;
hcd->self.root_hub->speed = USB_SPEED_HIGH;
} else {
the_controller.ss_hcd = hcd_to_dummy_hcd(hcd);
the_controller.ss_hcd->dum = &the_controller;
hcd->speed = HCD_USB3;
hcd->self.root_hub->speed = USB_SPEED_SUPER;
}
return 0;
}
static const struct hc_driver dummy_hcd = {
/* Change a group of bulk endpoints to support multiple stream IDs */
int dummy_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)
{
if (hcd->speed != HCD_USB3)
dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
"%s() - ERROR! Not supported for USB2.0 roothub\n",
__func__);
return 0;
}
/* Reverts a group of bulk endpoints back to not using stream IDs. */
int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags)
{
if (hcd->speed != HCD_USB3)
dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
"%s() - ERROR! Not supported for USB2.0 roothub\n",
__func__);
return 0;
}
static struct hc_driver dummy_hcd = {
.description = (char *) driver_name,
.product_desc = "Dummy host controller",
.hcd_priv_size = sizeof(struct dummy_hcd),
.flags = HCD_USB2,
.flags = HCD_USB3 | HCD_SHARED,
.reset = dummy_setup,
.start = dummy_start,
......@@ -1961,15 +2314,21 @@ static const struct hc_driver dummy_hcd = {
.hub_control = dummy_hub_control,
.bus_suspend = dummy_bus_suspend,
.bus_resume = dummy_bus_resume,
.alloc_streams = dummy_alloc_streams,
.free_streams = dummy_free_streams,
};
static int dummy_hcd_probe(struct platform_device *pdev)
{
struct usb_hcd *hs_hcd;
struct usb_hcd *ss_hcd;
int retval;
dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
if (!mod_data.is_super_speed)
dummy_hcd.flags = HCD_USB2;
hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
if (!hs_hcd)
return -ENOMEM;
......@@ -1978,8 +2337,28 @@ static int dummy_hcd_probe(struct platform_device *pdev)
retval = usb_add_hcd(hs_hcd, 0, 0);
if (retval != 0) {
usb_put_hcd(hs_hcd);
the_controller.hs_hcd = NULL;
return retval;
}
if (mod_data.is_super_speed) {
ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev,
dev_name(&pdev->dev), hs_hcd);
if (!ss_hcd) {
retval = -ENOMEM;
goto dealloc_usb2_hcd;
}
retval = usb_add_hcd(ss_hcd, 0, 0);
if (retval)
goto put_usb3_hcd;
}
return 0;
put_usb3_hcd:
usb_put_hcd(ss_hcd);
dealloc_usb2_hcd:
usb_put_hcd(hs_hcd);
the_controller.hs_hcd = the_controller.ss_hcd = NULL;
return retval;
}
......@@ -1988,9 +2367,17 @@ static int dummy_hcd_remove(struct platform_device *pdev)
struct dummy *dum;
dum = (hcd_to_dummy_hcd(platform_get_drvdata(pdev)))->dum;
if (dum->ss_hcd) {
usb_remove_hcd(dummy_hcd_to_hcd(dum->ss_hcd));
usb_put_hcd(dummy_hcd_to_hcd(dum->ss_hcd));
}
usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd));
usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd));
the_controller.hs_hcd = NULL;
the_controller.ss_hcd = NULL;
return 0;
}
......@@ -2065,7 +2452,8 @@ static int __init init (void)
retval = platform_device_add(the_hcd_pdev);
if (retval < 0)
goto err_add_hcd;
if (!the_controller.hs_hcd) {
if (!the_controller.hs_hcd ||
(!the_controller.ss_hcd && mod_data.is_super_speed)) {
/*
* The hcd was added successfully but its probe function failed
* for some reason.
......
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