|
@@ -237,6 +237,8 @@ struct dummy_hcd {
|
|
|
|
|
|
struct usb_device *udev;
|
|
|
struct list_head urbp_list;
|
|
|
+ struct urbp *next_frame_urbp;
|
|
|
+
|
|
|
u32 stream_en_ep;
|
|
|
u8 num_stream[30 / 2];
|
|
|
|
|
@@ -253,11 +255,13 @@ struct dummy {
|
|
|
*/
|
|
|
struct dummy_ep ep[DUMMY_ENDPOINTS];
|
|
|
int address;
|
|
|
+ int callback_usage;
|
|
|
struct usb_gadget gadget;
|
|
|
struct usb_gadget_driver *driver;
|
|
|
struct dummy_request fifo_req;
|
|
|
u8 fifo_buf[FIFO_SIZE];
|
|
|
u16 devstatus;
|
|
|
+ unsigned ints_enabled:1;
|
|
|
unsigned udc_suspended:1;
|
|
|
unsigned pullup:1;
|
|
|
|
|
@@ -375,11 +379,10 @@ static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
|
|
|
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)
|
|
|
+ if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) &&
|
|
|
+ (dum_hcd->port_status &
|
|
|
+ USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_U0 &&
|
|
|
+ dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
|
|
|
dum_hcd->active = 1;
|
|
|
}
|
|
|
} else {
|
|
@@ -440,18 +443,27 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
|
|
|
(~dum_hcd->old_status) & dum_hcd->port_status;
|
|
|
|
|
|
/* Report reset and disconnect events to the driver */
|
|
|
- if (dum->driver && (disconnect || reset)) {
|
|
|
+ if (dum->ints_enabled && (disconnect || reset)) {
|
|
|
stop_activity(dum);
|
|
|
+ ++dum->callback_usage;
|
|
|
+ spin_unlock(&dum->lock);
|
|
|
if (reset)
|
|
|
usb_gadget_udc_reset(&dum->gadget, dum->driver);
|
|
|
else
|
|
|
dum->driver->disconnect(&dum->gadget);
|
|
|
+ spin_lock(&dum->lock);
|
|
|
+ --dum->callback_usage;
|
|
|
}
|
|
|
- } else if (dum_hcd->active != dum_hcd->old_active) {
|
|
|
+ } else if (dum_hcd->active != dum_hcd->old_active &&
|
|
|
+ dum->ints_enabled) {
|
|
|
+ ++dum->callback_usage;
|
|
|
+ spin_unlock(&dum->lock);
|
|
|
if (dum_hcd->old_active && dum->driver->suspend)
|
|
|
dum->driver->suspend(&dum->gadget);
|
|
|
else if (!dum_hcd->old_active && dum->driver->resume)
|
|
|
dum->driver->resume(&dum->gadget);
|
|
|
+ spin_lock(&dum->lock);
|
|
|
+ --dum->callback_usage;
|
|
|
}
|
|
|
|
|
|
dum_hcd->old_status = dum_hcd->port_status;
|
|
@@ -972,8 +984,11 @@ static int dummy_udc_start(struct usb_gadget *g,
|
|
|
* can't enumerate without help from the driver we're binding.
|
|
|
*/
|
|
|
|
|
|
+ spin_lock_irq(&dum->lock);
|
|
|
dum->devstatus = 0;
|
|
|
dum->driver = driver;
|
|
|
+ dum->ints_enabled = 1;
|
|
|
+ spin_unlock_irq(&dum->lock);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -984,6 +999,16 @@ static int dummy_udc_stop(struct usb_gadget *g)
|
|
|
struct dummy *dum = dum_hcd->dum;
|
|
|
|
|
|
spin_lock_irq(&dum->lock);
|
|
|
+ dum->ints_enabled = 0;
|
|
|
+ stop_activity(dum);
|
|
|
+
|
|
|
+ /* emulate synchronize_irq(): wait for callbacks to finish */
|
|
|
+ while (dum->callback_usage > 0) {
|
|
|
+ spin_unlock_irq(&dum->lock);
|
|
|
+ usleep_range(1000, 2000);
|
|
|
+ spin_lock_irq(&dum->lock);
|
|
|
+ }
|
|
|
+
|
|
|
dum->driver = NULL;
|
|
|
spin_unlock_irq(&dum->lock);
|
|
|
|
|
@@ -1037,7 +1062,12 @@ static int dummy_udc_probe(struct platform_device *pdev)
|
|
|
memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
|
|
|
dum->gadget.name = gadget_name;
|
|
|
dum->gadget.ops = &dummy_ops;
|
|
|
- dum->gadget.max_speed = USB_SPEED_SUPER;
|
|
|
+ if (mod_data.is_super_speed)
|
|
|
+ dum->gadget.max_speed = USB_SPEED_SUPER;
|
|
|
+ else if (mod_data.is_high_speed)
|
|
|
+ dum->gadget.max_speed = USB_SPEED_HIGH;
|
|
|
+ else
|
|
|
+ dum->gadget.max_speed = USB_SPEED_FULL;
|
|
|
|
|
|
dum->gadget.dev.parent = &pdev->dev;
|
|
|
init_dummy_udc_hw(dum);
|
|
@@ -1246,6 +1276,8 @@ static int dummy_urb_enqueue(
|
|
|
|
|
|
list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
|
|
|
urb->hcpriv = urbp;
|
|
|
+ if (!dum_hcd->next_frame_urbp)
|
|
|
+ dum_hcd->next_frame_urbp = urbp;
|
|
|
if (usb_pipetype(urb->pipe) == PIPE_CONTROL)
|
|
|
urb->error_count = 1; /* mark as a new urb */
|
|
|
|
|
@@ -1521,6 +1553,8 @@ static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address)
|
|
|
if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
|
|
|
dum->ss_hcd : dum->hs_hcd)))
|
|
|
return NULL;
|
|
|
+ if (!dum->ints_enabled)
|
|
|
+ return NULL;
|
|
|
if ((address & ~USB_DIR_IN) == 0)
|
|
|
return &dum->ep[0];
|
|
|
for (i = 1; i < DUMMY_ENDPOINTS; i++) {
|
|
@@ -1762,6 +1796,7 @@ static void dummy_timer(unsigned long _dum_hcd)
|
|
|
spin_unlock_irqrestore(&dum->lock, flags);
|
|
|
return;
|
|
|
}
|
|
|
+ dum_hcd->next_frame_urbp = NULL;
|
|
|
|
|
|
for (i = 0; i < DUMMY_ENDPOINTS; i++) {
|
|
|
if (!ep_info[i].name)
|
|
@@ -1778,6 +1813,10 @@ restart:
|
|
|
int type;
|
|
|
int status = -EINPROGRESS;
|
|
|
|
|
|
+ /* stop when we reach URBs queued after the timer interrupt */
|
|
|
+ if (urbp == dum_hcd->next_frame_urbp)
|
|
|
+ break;
|
|
|
+
|
|
|
urb = urbp->urb;
|
|
|
if (urb->unlinked)
|
|
|
goto return_urb;
|
|
@@ -1857,10 +1896,12 @@ restart:
|
|
|
* until setup() returns; no reentrancy issues etc.
|
|
|
*/
|
|
|
if (value > 0) {
|
|
|
+ ++dum->callback_usage;
|
|
|
spin_unlock(&dum->lock);
|
|
|
value = dum->driver->setup(&dum->gadget,
|
|
|
&setup);
|
|
|
spin_lock(&dum->lock);
|
|
|
+ --dum->callback_usage;
|
|
|
|
|
|
if (value >= 0) {
|
|
|
/* no delays (max 64KB data stage) */
|
|
@@ -2561,8 +2602,6 @@ static struct hc_driver dummy_hcd = {
|
|
|
.product_desc = "Dummy host controller",
|
|
|
.hcd_priv_size = sizeof(struct dummy_hcd),
|
|
|
|
|
|
- .flags = HCD_USB3 | HCD_SHARED,
|
|
|
-
|
|
|
.reset = dummy_setup,
|
|
|
.start = dummy_start,
|
|
|
.stop = dummy_stop,
|
|
@@ -2591,8 +2630,12 @@ static int dummy_hcd_probe(struct platform_device *pdev)
|
|
|
dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
|
|
|
dum = *((void **)dev_get_platdata(&pdev->dev));
|
|
|
|
|
|
- if (!mod_data.is_super_speed)
|
|
|
+ if (mod_data.is_super_speed)
|
|
|
+ dummy_hcd.flags = HCD_USB3 | HCD_SHARED;
|
|
|
+ else if (mod_data.is_high_speed)
|
|
|
dummy_hcd.flags = HCD_USB2;
|
|
|
+ else
|
|
|
+ dummy_hcd.flags = HCD_USB11;
|
|
|
hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
|
|
|
if (!hs_hcd)
|
|
|
return -ENOMEM;
|