/
hub.c
6349 lines (5567 loc) · 181 KB
/
hub.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: GPL-2.0
/*
* USB hub driver.
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Gregory P. Smith
* (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/completion.h>
#include <linux/sched/mm.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/kcov.h>
#include <linux/ioctl.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
#include <linux/usb/onboard_hub.h>
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
#include <linux/kobject.h>
#include <linux/bitfield.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include "hub.h"
#include "otg_productlist.h"
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define USB_VENDOR_SMSC 0x0424
#define USB_PRODUCT_USB5534B 0x5534
#define USB_VENDOR_CYPRESS 0x04b4
#define USB_PRODUCT_CY7C65632 0x6570
#define USB_VENDOR_TEXAS_INSTRUMENTS 0x0451
#define USB_PRODUCT_TUSB8041_USB3 0x8140
#define USB_PRODUCT_TUSB8041_USB2 0x8142
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
#define HUB_QUIRK_DISABLE_AUTOSUSPEND 0x02
#define USB_TP_TRANSMISSION_DELAY 40 /* ns */
#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */
#define USB_PING_RESPONSE_TIME 400 /* ns */
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
static DEFINE_SPINLOCK(device_state_lock);
/* workqueue to process hub events */
static struct workqueue_struct *hub_wq;
static void hub_event(struct work_struct *work);
/* synchronize hub-port add/remove and peering operations */
DEFINE_MUTEX(usb_port_peer_mutex);
/* cycle leds on hubs that aren't blinking for attention */
static bool blinkenlights;
module_param(blinkenlights, bool, S_IRUGO);
MODULE_PARM_DESC(blinkenlights, "true to cycle leds on hubs");
/*
* Device SATA8000 FW1.0 from DATAST0R Technology Corp requires about
* 10 seconds to send reply for the initial 64-byte descriptor request.
*/
/* define initial 64-byte descriptor request timeout in milliseconds */
static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT;
module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(initial_descriptor_timeout,
"initial 64-byte descriptor request timeout in milliseconds "
"(default 5000 - 5.0 seconds)");
/*
* As of 2.6.10 we introduce a new USB device initialization scheme which
* closely resembles the way Windows works. Hopefully it will be compatible
* with a wider range of devices than the old scheme. However some previously
* working devices may start giving rise to "device not accepting address"
* errors; if that happens the user can try the old scheme by adjusting the
* following module parameters.
*
* For maximum flexibility there are two boolean parameters to control the
* hub driver's behavior. On the first initialization attempt, if the
* "old_scheme_first" parameter is set then the old scheme will be used,
* otherwise the new scheme is used. If that fails and "use_both_schemes"
* is set, then the driver will make another attempt, using the other scheme.
*/
static bool old_scheme_first;
module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(old_scheme_first,
"start with the old device initialization scheme");
static bool use_both_schemes = true;
module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(use_both_schemes,
"try the other device initialization scheme if the "
"first one fails");
/* Mutual exclusion for EHCI CF initialization. This interferes with
* port reset on some companion controllers.
*/
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_TIMEOUT 2000
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
u16 portstatus);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
if (hub_is_superspeedplus(hub->hdev))
return "10.0 Gb/s";
if (hub_is_superspeed(hub->hdev))
return "5.0 Gb/s";
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
return "480 Mb/s";
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
else
return "12 Mb/s";
}
/* Note that hdev or one of its children must be locked! */
struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
{
if (!hdev || !hdev->actconfig || !hdev->maxchild)
return NULL;
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
int usb_device_supports_lpm(struct usb_device *udev)
{
/* Some devices have trouble with LPM */
if (udev->quirks & USB_QUIRK_NO_LPM)
return 0;
/* USB 2.1 (and greater) devices indicate LPM support through
* their USB 2.0 Extended Capabilities BOS descriptor.
*/
if (udev->speed == USB_SPEED_HIGH || udev->speed == USB_SPEED_FULL) {
if (udev->bos->ext_cap &&
(USB_LPM_SUPPORT &
le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
return 1;
return 0;
}
/*
* According to the USB 3.0 spec, all USB 3.0 devices must support LPM.
* However, there are some that don't, and they set the U1/U2 exit
* latencies to zero.
*/
if (!udev->bos->ss_cap) {
dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
return 0;
}
if (udev->bos->ss_cap->bU1devExitLat == 0 &&
udev->bos->ss_cap->bU2DevExitLat == 0) {
if (udev->parent)
dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n");
else
dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n");
return 0;
}
if (!udev->parent || udev->parent->lpm_capable)
return 1;
return 0;
}
/*
* Set the Maximum Exit Latency (MEL) for the host to wakup up the path from
* U1/U2, send a PING to the device and receive a PING_RESPONSE.
* See USB 3.1 section C.1.5.2
*/
static void usb_set_lpm_mel(struct usb_device *udev,
struct usb3_lpm_parameters *udev_lpm_params,
unsigned int udev_exit_latency,
struct usb_hub *hub,
struct usb3_lpm_parameters *hub_lpm_params,
unsigned int hub_exit_latency)
{
unsigned int total_mel;
/*
* tMEL1. time to transition path from host to device into U0.
* MEL for parent already contains the delay up to parent, so only add
* the exit latency for the last link (pick the slower exit latency),
* and the hub header decode latency. See USB 3.1 section C 2.2.1
* Store MEL in nanoseconds
*/
total_mel = hub_lpm_params->mel +
max(udev_exit_latency, hub_exit_latency) * 1000 +
hub->descriptor->u.ss.bHubHdrDecLat * 100;
/*
* tMEL2. Time to submit PING packet. Sum of tTPTransmissionDelay for
* each link + wHubDelay for each hub. Add only for last link.
* tMEL4, the time for PING_RESPONSE to traverse upstream is similar.
* Multiply by 2 to include it as well.
*/
total_mel += (__le16_to_cpu(hub->descriptor->u.ss.wHubDelay) +
USB_TP_TRANSMISSION_DELAY) * 2;
/*
* tMEL3, tPingResponse. Time taken by device to generate PING_RESPONSE
* after receiving PING. Also add 2100ns as stated in USB 3.1 C 1.5.2.4
* to cover the delay if the PING_RESPONSE is queued behind a Max Packet
* Size DP.
* Note these delays should be added only once for the entire path, so
* add them to the MEL of the device connected to the roothub.
*/
if (!hub->hdev->parent)
total_mel += USB_PING_RESPONSE_TIME + 2100;
udev_lpm_params->mel = total_mel;
}
/*
* Set the maximum Device to Host Exit Latency (PEL) for the device to initiate
* a transition from either U1 or U2.
*/
static void usb_set_lpm_pel(struct usb_device *udev,
struct usb3_lpm_parameters *udev_lpm_params,
unsigned int udev_exit_latency,
struct usb_hub *hub,
struct usb3_lpm_parameters *hub_lpm_params,
unsigned int hub_exit_latency,
unsigned int port_to_port_exit_latency)
{
unsigned int first_link_pel;
unsigned int hub_pel;
/*
* First, the device sends an LFPS to transition the link between the
* device and the parent hub into U0. The exit latency is the bigger of
* the device exit latency or the hub exit latency.
*/
if (udev_exit_latency > hub_exit_latency)
first_link_pel = udev_exit_latency * 1000;
else
first_link_pel = hub_exit_latency * 1000;
/*
* When the hub starts to receive the LFPS, there is a slight delay for
* it to figure out that one of the ports is sending an LFPS. Then it
* will forward the LFPS to its upstream link. The exit latency is the
* delay, plus the PEL that we calculated for this hub.
*/
hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel;
/*
* According to figure C-7 in the USB 3.0 spec, the PEL for this device
* is the greater of the two exit latencies.
*/
if (first_link_pel > hub_pel)
udev_lpm_params->pel = first_link_pel;
else
udev_lpm_params->pel = hub_pel;
}
/*
* Set the System Exit Latency (SEL) to indicate the total worst-case time from
* when a device initiates a transition to U0, until when it will receive the
* first packet from the host controller.
*
* Section C.1.5.1 describes the four components to this:
* - t1: device PEL
* - t2: time for the ERDY to make it from the device to the host.
* - t3: a host-specific delay to process the ERDY.
* - t4: time for the packet to make it from the host to the device.
*
* t3 is specific to both the xHCI host and the platform the host is integrated
* into. The Intel HW folks have said it's negligible, FIXME if a different
* vendor says otherwise.
*/
static void usb_set_lpm_sel(struct usb_device *udev,
struct usb3_lpm_parameters *udev_lpm_params)
{
struct usb_device *parent;
unsigned int num_hubs;
unsigned int total_sel;
/* t1 = device PEL */
total_sel = udev_lpm_params->pel;
/* How many external hubs are in between the device & the root port. */
for (parent = udev->parent, num_hubs = 0; parent->parent;
parent = parent->parent)
num_hubs++;
/* t2 = 2.1us + 250ns * (num_hubs - 1) */
if (num_hubs > 0)
total_sel += 2100 + 250 * (num_hubs - 1);
/* t4 = 250ns * num_hubs */
total_sel += 250 * num_hubs;
udev_lpm_params->sel = total_sel;
}
static void usb_set_lpm_parameters(struct usb_device *udev)
{
struct usb_hub *hub;
unsigned int port_to_port_delay;
unsigned int udev_u1_del;
unsigned int udev_u2_del;
unsigned int hub_u1_del;
unsigned int hub_u2_del;
if (!udev->lpm_capable || udev->speed < USB_SPEED_SUPER)
return;
hub = usb_hub_to_struct_hub(udev->parent);
/* It doesn't take time to transition the roothub into U0, since it
* doesn't have an upstream link.
*/
if (!hub)
return;
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
hub, &udev->parent->u1_params, hub_u1_del);
usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del,
hub, &udev->parent->u2_params, hub_u2_del);
/*
* Appendix C, section C.2.2.2, says that there is a slight delay from
* when the parent hub notices the downstream port is trying to
* transition to U0 to when the hub initiates a U0 transition on its
* upstream port. The section says the delays are tPort2PortU1EL and
* tPort2PortU2EL, but it doesn't define what they are.
*
* The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking
* about the same delays. Use the maximum delay calculations from those
* sections. For U1, it's tHubPort2PortExitLat, which is 1us max. For
* U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat. I
* assume the device exit latencies they are talking about are the hub
* exit latencies.
*
* What do we do if the U2 exit latency is less than the U1 exit
* latency? It's possible, although not likely...
*/
port_to_port_delay = 1;
usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del,
hub, &udev->parent->u1_params, hub_u1_del,
port_to_port_delay);
if (hub_u2_del > hub_u1_del)
port_to_port_delay = 1 + hub_u2_del - hub_u1_del;
else
port_to_port_delay = 1 + hub_u1_del;
usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del,
hub, &udev->parent->u2_params, hub_u2_del,
port_to_port_delay);
/* Now that we've got PEL, calculate SEL. */
usb_set_lpm_sel(udev, &udev->u1_params);
usb_set_lpm_sel(udev, &udev->u2_params);
}
/* USB 2.0 spec Section 11.24.4.5 */
static int get_hub_descriptor(struct usb_device *hdev,
struct usb_hub_descriptor *desc)
{
int i, ret, size;
unsigned dtype;
if (hub_is_superspeed(hdev)) {
dtype = USB_DT_SS_HUB;
size = USB_DT_SS_HUB_SIZE;
} else {
dtype = USB_DT_HUB;
size = sizeof(struct usb_hub_descriptor);
}
for (i = 0; i < 3; i++) {
ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
dtype << 8, 0, desc, size,
USB_CTRL_GET_TIMEOUT);
if (hub_is_superspeed(hdev)) {
if (ret == size)
return ret;
} else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) {
/* Make sure we have the DeviceRemovable field. */
size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1;
if (ret < size)
return -EMSGSIZE;
return ret;
}
}
return -EINVAL;
}
/*
* USB 2.0 spec Section 11.24.2.1
*/
static int clear_hub_feature(struct usb_device *hdev, int feature)
{
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, 1000);
}
/*
* USB 2.0 spec Section 11.24.2.2
*/
int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
{
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
NULL, 0, 1000);
}
/*
* USB 2.0 spec Section 11.24.2.13
*/
static int set_port_feature(struct usb_device *hdev, int port1, int feature)
{
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
NULL, 0, 1000);
}
static char *to_led_name(int selector)
{
switch (selector) {
case HUB_LED_AMBER:
return "amber";
case HUB_LED_GREEN:
return "green";
case HUB_LED_OFF:
return "off";
case HUB_LED_AUTO:
return "auto";
default:
return "??";
}
}
/*
* USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7
* for info about using port indicators
*/
static void set_port_led(struct usb_hub *hub, int port1, int selector)
{
struct usb_port *port_dev = hub->ports[port1 - 1];
int status;
status = set_port_feature(hub->hdev, (selector << 8) | port1,
USB_PORT_FEAT_INDICATOR);
dev_dbg(&port_dev->dev, "indicator %s status %d\n",
to_led_name(selector), status);
}
#define LED_CYCLE_PERIOD ((2*HZ)/3)
static void led_work(struct work_struct *work)
{
struct usb_hub *hub =
container_of(work, struct usb_hub, leds.work);
struct usb_device *hdev = hub->hdev;
unsigned i;
unsigned changed = 0;
int cursor = -1;
if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)
return;
for (i = 0; i < hdev->maxchild; i++) {
unsigned selector, mode;
/* 30%-50% duty cycle */
switch (hub->indicator[i]) {
/* cycle marker */
case INDICATOR_CYCLE:
cursor = i;
selector = HUB_LED_AUTO;
mode = INDICATOR_AUTO;
break;
/* blinking green = sw attention */
case INDICATOR_GREEN_BLINK:
selector = HUB_LED_GREEN;
mode = INDICATOR_GREEN_BLINK_OFF;
break;
case INDICATOR_GREEN_BLINK_OFF:
selector = HUB_LED_OFF;
mode = INDICATOR_GREEN_BLINK;
break;
/* blinking amber = hw attention */
case INDICATOR_AMBER_BLINK:
selector = HUB_LED_AMBER;
mode = INDICATOR_AMBER_BLINK_OFF;
break;
case INDICATOR_AMBER_BLINK_OFF:
selector = HUB_LED_OFF;
mode = INDICATOR_AMBER_BLINK;
break;
/* blink green/amber = reserved */
case INDICATOR_ALT_BLINK:
selector = HUB_LED_GREEN;
mode = INDICATOR_ALT_BLINK_OFF;
break;
case INDICATOR_ALT_BLINK_OFF:
selector = HUB_LED_AMBER;
mode = INDICATOR_ALT_BLINK;
break;
default:
continue;
}
if (selector != HUB_LED_AUTO)
changed = 1;
set_port_led(hub, i + 1, selector);
hub->indicator[i] = mode;
}
if (!changed && blinkenlights) {
cursor++;
cursor %= hdev->maxchild;
set_port_led(hub, cursor + 1, HUB_LED_GREEN);
hub->indicator[cursor] = INDICATOR_CYCLE;
changed++;
}
if (changed)
queue_delayed_work(system_power_efficient_wq,
&hub->leds, LED_CYCLE_PERIOD);
}
/* use a short timeout for hub/port status fetches */
#define USB_STS_TIMEOUT 1000
#define USB_STS_RETRIES 5
/*
* USB 2.0 spec Section 11.24.2.6
*/
static int get_hub_status(struct usb_device *hdev,
struct usb_hub_status *data)
{
int i, status = -ETIMEDOUT;
for (i = 0; i < USB_STS_RETRIES &&
(status == -ETIMEDOUT || status == -EPIPE); i++) {
status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
data, sizeof(*data), USB_STS_TIMEOUT);
}
return status;
}
/*
* USB 2.0 spec Section 11.24.2.7
* USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6
*/
static int get_port_status(struct usb_device *hdev, int port1,
void *data, u16 value, u16 length)
{
int i, status = -ETIMEDOUT;
for (i = 0; i < USB_STS_RETRIES &&
(status == -ETIMEDOUT || status == -EPIPE); i++) {
status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
port1, data, length, USB_STS_TIMEOUT);
}
return status;
}
static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
u16 *status, u16 *change, u32 *ext_status)
{
int ret;
int len = 4;
if (type != HUB_PORT_STATUS)
len = 8;
mutex_lock(&hub->status_mutex);
ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
if (ret < len) {
if (ret != -ENODEV)
dev_err(hub->intfdev,
"%s failed (err = %d)\n", __func__, ret);
if (ret >= 0)
ret = -EIO;
} else {
*status = le16_to_cpu(hub->status->port.wPortStatus);
*change = le16_to_cpu(hub->status->port.wPortChange);
if (type != HUB_PORT_STATUS && ext_status)
*ext_status = le32_to_cpu(
hub->status->port.dwExtPortStatus);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
return ret;
}
int usb_hub_port_status(struct usb_hub *hub, int port1,
u16 *status, u16 *change)
{
return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
status, change, NULL);
}
static void hub_resubmit_irq_urb(struct usb_hub *hub)
{
unsigned long flags;
int status;
spin_lock_irqsave(&hub->irq_urb_lock, flags);
if (hub->quiescing) {
spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
return;
}
status = usb_submit_urb(hub->urb, GFP_ATOMIC);
if (status && status != -ENODEV && status != -EPERM &&
status != -ESHUTDOWN) {
dev_err(hub->intfdev, "resubmit --> %d\n", status);
mod_timer(&hub->irq_urb_retry, jiffies + HZ);
}
spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
}
static void hub_retry_irq_urb(struct timer_list *t)
{
struct usb_hub *hub = from_timer(hub, t, irq_urb_retry);
hub_resubmit_irq_urb(hub);
}
static void kick_hub_wq(struct usb_hub *hub)
{
struct usb_interface *intf;
if (hub->disconnected || work_pending(&hub->events))
return;
/*
* Suppress autosuspend until the event is proceed.
*
* Be careful and make sure that the symmetric operation is
* always called. We are here only when there is no pending
* work for this hub. Therefore put the interface either when
* the new work is called or when it is canceled.
*/
intf = to_usb_interface(hub->intfdev);
usb_autopm_get_interface_no_resume(intf);
kref_get(&hub->kref);
if (queue_work(hub_wq, &hub->events))
return;
/* the work has already been scheduled */
usb_autopm_put_interface_async(intf);
kref_put(&hub->kref, hub_release);
}
void usb_kick_hub_wq(struct usb_device *hdev)
{
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
if (hub)
kick_hub_wq(hub);
}
/*
* Let the USB core know that a USB 3.0 device has sent a Function Wake Device
* Notification, which indicates it had initiated remote wakeup.
*
* USB 3.0 hubs do not report the port link state change from U3 to U0 when the
* device initiates resume, so the USB core will not receive notice of the
* resume through the normal hub interrupt URB.
*/
void usb_wakeup_notification(struct usb_device *hdev,
unsigned int portnum)
{
struct usb_hub *hub;
struct usb_port *port_dev;
if (!hdev)
return;
hub = usb_hub_to_struct_hub(hdev);
if (hub) {
port_dev = hub->ports[portnum - 1];
if (port_dev && port_dev->child)
pm_wakeup_event(&port_dev->child->dev, 0);
set_bit(portnum, hub->wakeup_bits);
kick_hub_wq(hub);
}
}
EXPORT_SYMBOL_GPL(usb_wakeup_notification);
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
unsigned i;
unsigned long bits;
switch (status) {
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
return;
default: /* presumably an error */
/* Cause a hub reset after 10 consecutive errors */
dev_dbg(hub->intfdev, "transfer --> %d\n", status);
if ((++hub->nerrors < 10) || hub->error)
goto resubmit;
hub->error = status;
fallthrough;
/* let hub_wq handle things */
case 0: /* we got data: port status changed */
bits = 0;
for (i = 0; i < urb->actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)[i]))
<< (i*8);
hub->event_bits[0] = bits;
break;
}
hub->nerrors = 0;
/* Something happened, let hub_wq figure it out */
kick_hub_wq(hub);
resubmit:
hub_resubmit_irq_urb(hub);
}
/* USB 2.0 spec Section 11.24.2.3 */
static inline int
hub_clear_tt_buffer(struct usb_device *hdev, u16 devinfo, u16 tt)
{
/* Need to clear both directions for control ep */
if (((devinfo >> 11) & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_CONTROL) {
int status = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
HUB_CLEAR_TT_BUFFER, USB_RT_PORT,
devinfo ^ 0x8000, tt, NULL, 0, 1000);
if (status)
return status;
}
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,
tt, NULL, 0, 1000);
}
/*
* enumeration blocks hub_wq for a long time. we use keventd instead, since
* long blocking there is the exception, not the rule. accordingly, HCDs
* talking to TTs must queue control transfers (not just bulk and iso), so
* both can talk to the same hub concurrently.
*/
static void hub_tt_work(struct work_struct *work)
{
struct usb_hub *hub =
container_of(work, struct usb_hub, tt.clear_work);
unsigned long flags;
spin_lock_irqsave(&hub->tt.lock, flags);
while (!list_empty(&hub->tt.clear_list)) {
struct list_head *next;
struct usb_tt_clear *clear;
struct usb_device *hdev = hub->hdev;
const struct hc_driver *drv;
int status;
next = hub->tt.clear_list.next;
clear = list_entry(next, struct usb_tt_clear, clear_list);
list_del(&clear->clear_list);
/* drop lock so HCD can concurrently report other TT errors */
spin_unlock_irqrestore(&hub->tt.lock, flags);
status = hub_clear_tt_buffer(hdev, clear->devinfo, clear->tt);
if (status && status != -ENODEV)
dev_err(&hdev->dev,
"clear tt %d (%04x) error %d\n",
clear->tt, clear->devinfo, status);
/* Tell the HCD, even if the operation failed */
drv = clear->hcd->driver;
if (drv->clear_tt_buffer_complete)
(drv->clear_tt_buffer_complete)(clear->hcd, clear->ep);
kfree(clear);
spin_lock_irqsave(&hub->tt.lock, flags);
}
spin_unlock_irqrestore(&hub->tt.lock, flags);
}
/**
* usb_hub_set_port_power - control hub port's power state
* @hdev: USB device belonging to the usb hub
* @hub: target hub
* @port1: port index
* @set: expected status
*
* call this function to control port's power via setting or
* clearing the port's PORT_POWER feature.
*
* Return: 0 if successful. A negative error code otherwise.
*/
int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
int port1, bool set)
{
int ret;
if (set)
ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
else
ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (ret)
return ret;
if (set)
set_bit(port1, hub->power_bits);
else
clear_bit(port1, hub->power_bits);
return 0;
}
/**
* usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
* @urb: an URB associated with the failed or incomplete split transaction
*
* High speed HCDs use this to tell the hub driver that some split control or
* bulk transaction failed in a way that requires clearing internal state of
* a transaction translator. This is normally detected (and reported) from
* interrupt context.
*
* It may not be possible for that hub to handle additional full (or low)
* speed transactions until that state is fully cleared out.
*
* Return: 0 if successful. A negative error code otherwise.
*/
int usb_hub_clear_tt_buffer(struct urb *urb)
{
struct usb_device *udev = urb->dev;
int pipe = urb->pipe;
struct usb_tt *tt = udev->tt;
unsigned long flags;
struct usb_tt_clear *clear;
/* we've got to cope with an arbitrary number of pending TT clears,
* since each TT has "at least two" buffers that can need it (and
* there can be many TTs per hub). even if they're uncommon.
*/
clear = kmalloc(sizeof *clear, GFP_ATOMIC);
if (clear == NULL) {
dev_err(&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
/* FIXME recover somehow ... RESET_TT? */
return -ENOMEM;
}
/* info that CLEAR_TT_BUFFER needs */
clear->tt = tt->multi ? udev->ttport : 1;
clear->devinfo = usb_pipeendpoint (pipe);
clear->devinfo |= ((u16)udev->devaddr) << 4;
clear->devinfo |= usb_pipecontrol(pipe)
? (USB_ENDPOINT_XFER_CONTROL << 11)
: (USB_ENDPOINT_XFER_BULK << 11);
if (usb_pipein(pipe))
clear->devinfo |= 1 << 15;
/* info for completion callback */
clear->hcd = bus_to_hcd(udev->bus);
clear->ep = urb->ep;
/* tell keventd to clear state for this TT */
spin_lock_irqsave(&tt->lock, flags);
list_add_tail(&clear->clear_list, &tt->clear_list);
schedule_work(&tt->clear_work);
spin_unlock_irqrestore(&tt->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);
static void hub_power_on(struct usb_hub *hub, bool do_delay)
{
int port1;
/* Enable power on each port. Some hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are
* USB 2.0 hubs. Some hubs do not implement port-power switching
* but only emulate it. In all cases, the ports won't work
* unless we send these messages to the hub.
*/
if (hub_is_port_power_switchable(hub))
dev_dbg(hub->intfdev, "enabling power on all ports\n");
else
dev_dbg(hub->intfdev, "trying to enable port power on "
"non-switchable hub\n");
for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
if (test_bit(port1, hub->power_bits))
set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
else
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_POWER);
if (do_delay)
msleep(hub_power_on_good_delay(hub));
}
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
int ret;
mutex_lock(&hub->status_mutex);
ret = get_hub_status(hub->hdev, &hub->status->hub);
if (ret < 0) {
if (ret != -ENODEV)
dev_err(hub->intfdev,
"%s failed (err = %d)\n", __func__, ret);
} else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
*change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
return ret;
}
static int hub_set_port_link_state(struct usb_hub *hub, int port1,
unsigned int link_status)
{
return set_port_feature(hub->hdev,
port1 | (link_status << 3),
USB_PORT_FEAT_LINK_STATE);
}
/*
* Disable a port and mark a logical connect-change event, so that some
* time later hub_wq will disconnect() any existing usb_device on the port
* and will re-enumerate if there actually is a device attached.
*/
static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
{
dev_dbg(&hub->ports[port1 - 1]->dev, "logical disconnect\n");
hub_port_disable(hub, port1, 1);
/* FIXME let caller ask to power down the port:
* - some devices won't enumerate without a VBUS power cycle
* - SRP saves power that way
* - ... new call, TBD ...
* That's easy if this hub can switch power per-port, and
* hub_wq reactivates the port later (timer, SRP, etc).
* Powerdown must be optional, because of reset/DFU.
*/
set_bit(port1, hub->change_bits);
kick_hub_wq(hub);
}
/**
* usb_remove_device - disable a device's port on its parent hub
* @udev: device to be disabled and removed
* Context: @udev locked, must be able to sleep.
*
* After @udev's port has been disabled, hub_wq is notified and it will
* see that the device has been disconnected. When the device is
* physically unplugged and something is plugged in, the events will
* be received and processed normally.
*
* Return: 0 if successful. A negative error code otherwise.
*/
int usb_remove_device(struct usb_device *udev)
{