@@ -77,6 +77,7 @@ struct usb_hub {
77
77
unsigned has_indicators :1 ;
78
78
u8 indicator [USB_MAXCHILDREN ];
79
79
struct delayed_work leds ;
80
+ struct delayed_work init_work ;
80
81
};
81
82
82
83
@@ -515,10 +516,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
515
516
}
516
517
EXPORT_SYMBOL_GPL (usb_hub_tt_clear_buffer );
517
518
518
- static void hub_power_on (struct usb_hub * hub )
519
+ /* If do_delay is false, return the number of milliseconds the caller
520
+ * needs to delay.
521
+ */
522
+ static unsigned hub_power_on (struct usb_hub * hub , bool do_delay )
519
523
{
520
524
int port1 ;
521
525
unsigned pgood_delay = hub -> descriptor -> bPwrOn2PwrGood * 2 ;
526
+ unsigned delay ;
522
527
u16 wHubCharacteristics =
523
528
le16_to_cpu (hub -> descriptor -> wHubCharacteristics );
524
529
@@ -537,7 +542,10 @@ static void hub_power_on(struct usb_hub *hub)
537
542
set_port_feature (hub -> hdev , port1 , USB_PORT_FEAT_POWER );
538
543
539
544
/* Wait at least 100 msec for power to become stable */
540
- msleep (max (pgood_delay , (unsigned ) 100 ));
545
+ delay = max (pgood_delay , (unsigned ) 100 );
546
+ if (do_delay )
547
+ msleep (delay );
548
+ return delay ;
541
549
}
542
550
543
551
static int hub_hub_status (struct usb_hub * hub ,
@@ -599,21 +607,55 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
599
607
}
600
608
601
609
enum hub_activation_type {
602
- HUB_INIT , HUB_POST_RESET , HUB_RESUME , HUB_RESET_RESUME
610
+ HUB_INIT , HUB_INIT2 , HUB_INIT3 ,
611
+ HUB_POST_RESET , HUB_RESUME , HUB_RESET_RESUME ,
603
612
};
604
613
614
+ static void hub_init_func2 (struct work_struct * ws );
615
+ static void hub_init_func3 (struct work_struct * ws );
616
+
605
617
static void hub_activate (struct usb_hub * hub , enum hub_activation_type type )
606
618
{
607
619
struct usb_device * hdev = hub -> hdev ;
608
620
int port1 ;
609
621
int status ;
610
622
bool need_debounce_delay = false;
623
+ unsigned delay ;
624
+
625
+ /* Continue a partial initialization */
626
+ if (type == HUB_INIT2 )
627
+ goto init2 ;
628
+ if (type == HUB_INIT3 )
629
+ goto init3 ;
611
630
612
631
/* After a resume, port power should still be on.
613
632
* For any other type of activation, turn it on.
614
633
*/
615
- if (type != HUB_RESUME )
616
- hub_power_on (hub );
634
+ if (type != HUB_RESUME ) {
635
+
636
+ /* Speed up system boot by using a delayed_work for the
637
+ * hub's initial power-up delays. This is pretty awkward
638
+ * and the implementation looks like a home-brewed sort of
639
+ * setjmp/longjmp, but it saves at least 100 ms for each
640
+ * root hub (assuming usbcore is compiled into the kernel
641
+ * rather than as a module). It adds up.
642
+ *
643
+ * This can't be done for HUB_RESUME or HUB_RESET_RESUME
644
+ * because for those activation types the ports have to be
645
+ * operational when we return. In theory this could be done
646
+ * for HUB_POST_RESET, but it's easier not to.
647
+ */
648
+ if (type == HUB_INIT ) {
649
+ delay = hub_power_on (hub , false);
650
+ PREPARE_DELAYED_WORK (& hub -> init_work , hub_init_func2 );
651
+ schedule_delayed_work (& hub -> init_work ,
652
+ msecs_to_jiffies (delay ));
653
+ return ; /* Continues at init2: below */
654
+ } else {
655
+ hub_power_on (hub , true);
656
+ }
657
+ }
658
+ init2 :
617
659
618
660
/* Check each port and set hub->change_bits to let khubd know
619
661
* which ports need attention.
@@ -692,9 +734,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
692
734
* If any port-status changes do occur during this delay, khubd
693
735
* will see them later and handle them normally.
694
736
*/
695
- if (need_debounce_delay )
696
- msleep (HUB_DEBOUNCE_STABLE );
697
-
737
+ if (need_debounce_delay ) {
738
+ delay = HUB_DEBOUNCE_STABLE ;
739
+
740
+ /* Don't do a long sleep inside a workqueue routine */
741
+ if (type == HUB_INIT2 ) {
742
+ PREPARE_DELAYED_WORK (& hub -> init_work , hub_init_func3 );
743
+ schedule_delayed_work (& hub -> init_work ,
744
+ msecs_to_jiffies (delay ));
745
+ return ; /* Continues at init3: below */
746
+ } else {
747
+ msleep (delay );
748
+ }
749
+ }
750
+ init3 :
698
751
hub -> quiescing = 0 ;
699
752
700
753
status = usb_submit_urb (hub -> urb , GFP_NOIO );
@@ -707,6 +760,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
707
760
kick_khubd (hub );
708
761
}
709
762
763
+ /* Implement the continuations for the delays above */
764
+ static void hub_init_func2 (struct work_struct * ws )
765
+ {
766
+ struct usb_hub * hub = container_of (ws , struct usb_hub , init_work .work );
767
+
768
+ hub_activate (hub , HUB_INIT2 );
769
+ }
770
+
771
+ static void hub_init_func3 (struct work_struct * ws )
772
+ {
773
+ struct usb_hub * hub = container_of (ws , struct usb_hub , init_work .work );
774
+
775
+ hub_activate (hub , HUB_INIT3 );
776
+ }
777
+
710
778
enum hub_quiescing_type {
711
779
HUB_DISCONNECT , HUB_PRE_RESET , HUB_SUSPEND
712
780
};
@@ -716,6 +784,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
716
784
struct usb_device * hdev = hub -> hdev ;
717
785
int i ;
718
786
787
+ cancel_delayed_work_sync (& hub -> init_work );
788
+
719
789
/* khubd and related activity won't re-trigger */
720
790
hub -> quiescing = 1 ;
721
791
@@ -1099,6 +1169,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
1099
1169
hub -> intfdev = & intf -> dev ;
1100
1170
hub -> hdev = hdev ;
1101
1171
INIT_DELAYED_WORK (& hub -> leds , led_work );
1172
+ INIT_DELAYED_WORK (& hub -> init_work , NULL );
1102
1173
usb_get_intf (intf );
1103
1174
1104
1175
usb_set_intfdata (intf , hub );
@@ -3035,7 +3106,7 @@ static void hub_events(void)
3035
3106
i );
3036
3107
clear_port_feature (hdev , i ,
3037
3108
USB_PORT_FEAT_C_OVER_CURRENT );
3038
- hub_power_on (hub );
3109
+ hub_power_on (hub , true );
3039
3110
}
3040
3111
3041
3112
if (portchange & USB_PORT_STAT_C_RESET ) {
@@ -3070,7 +3141,7 @@ static void hub_events(void)
3070
3141
dev_dbg (hub_dev , "overcurrent change\n" );
3071
3142
msleep (500 ); /* Cool down */
3072
3143
clear_hub_feature (hdev , C_HUB_OVER_CURRENT );
3073
- hub_power_on (hub );
3144
+ hub_power_on (hub , true );
3074
3145
}
3075
3146
}
3076
3147
0 commit comments