in gadget/udc/dummy_hcd.c [2092:2360]
static int dummy_hub_control(
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct dummy_hcd *dum_hcd;
int retval = 0;
unsigned long flags;
if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
dum_hcd = hcd_to_dummy_hcd(hcd);
spin_lock_irqsave(&dum_hcd->dum->lock, flags);
switch (typeReq) {
case ClearHubFeature:
break;
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;
dum_hcd->re_timeout = jiffies +
msecs_to_jiffies(20);
}
break;
case USB_PORT_FEAT_POWER:
dev_dbg(dummy_dev(dum_hcd), "power-off\n");
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_ENABLE:
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_SUSPEND:
/* Not allowed for USB-3 */
if (hcd->speed == HCD_USB3)
goto error;
fallthrough;
case USB_PORT_FEAT_C_CONNECTION:
case USB_PORT_FEAT_C_RESET:
dum_hcd->port_status &= ~(1 << wValue);
set_link_state(dum_hcd);
break;
default:
/* Disallow INDICATOR and C_OVER_CURRENT */
goto error;
}
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 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:
*(__le32 *) buf = cpu_to_le32(0);
break;
case GetPortStatus:
if (wIndex != 1)
retval = -EPIPE;
/* whoever resets or resumes must GetPortStatus to
* complete it!!
*/
if (dum_hcd->resuming &&
time_after_eq(jiffies, dum_hcd->re_timeout)) {
dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND;
}
if ((dum_hcd->port_status & USB_PORT_STAT_RESET) != 0 &&
time_after_eq(jiffies, dum_hcd->re_timeout)) {
dum_hcd->port_status |= (USB_PORT_STAT_C_RESET << 16);
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 |=
USB_PORT_STAT_HIGH_SPEED;
break;
case USB_SPEED_LOW:
dum_hcd->dum->gadget.ep0->
maxpacket = 8;
dum_hcd->port_status |=
USB_PORT_STAT_LOW_SPEED;
break;
default:
break;
}
}
}
}
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);
break;
case SetHubFeature:
retval = -EPIPE;
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;
/* HNP would happen here; for now we
* assume b_bus_req is always true.
*/
set_link_state(dum_hcd);
if (((1 << USB_DEVICE_B_HNP_ENABLE)
& dum_hcd->dum->devstatus) != 0)
dev_dbg(dummy_dev(dum_hcd),
"no HNP yet!\n");
}
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;
}
fallthrough;
case USB_PORT_FEAT_RESET:
if (!(dum_hcd->port_status & USB_PORT_STAT_CONNECTION))
break;
/* if it's already enabled, disable */
if (hcd->speed == HCD_USB3) {
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);
dum_hcd->port_status |= USB_PORT_STAT_RESET;
}
/*
* We want to reset device status. All but the
* Self powered feature
*/
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);
set_link_state(dum_hcd);
break;
case USB_PORT_FEAT_C_CONNECTION:
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_SUSPEND:
/* Not allowed for USB-3, and ignored for USB-2 */
if (hcd->speed == HCD_USB3)
goto error;
break;
default:
/* Disallow TEST, INDICATOR, and C_OVER_CURRENT */
goto error;
}
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;
}
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
if ((dum_hcd->port_status & PORT_C_MASK) != 0)
usb_hcd_poll_rh_status(hcd);
return retval;
}