summaryrefslogtreecommitdiffstats
path: root/scripts/mod/file2alias.c
blob: d4dc222a74f33c69fd17dbb7170c41731933aa2b (plain)
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
/* Simple code to turn various tables in an ELF file into alias definitions.
 * This deals with kernel datastructures where they should be
 * dealt with: in the kernel source.
 *
 * Copyright 2002-2003  Rusty Russell, IBM Corporation
 *           2003       Kai Germaschewski
 *
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 */

#include "modpost.h"

/* We use the ELF typedefs for kernel_ulong_t but bite the bullet and
 * use either stdint.h or inttypes.h for the rest. */
#if KERNEL_ELFCLASS == ELFCLASS32
typedef Elf32_Addr	kernel_ulong_t;
#define BITS_PER_LONG 32
#else
typedef Elf64_Addr	kernel_ulong_t;
#define BITS_PER_LONG 64
#endif
#ifdef __sun__
#include <inttypes.h>
#else
#include <stdint.h>
#endif

#include <ctype.h>

typedef uint32_t	__u32;
typedef uint16_t	__u16;
typedef unsigned char	__u8;

/* Big exception to the "don't include kernel headers into userspace, which
 * even potentially has different endianness and word sizes, since
 * we handle those differences explicitly below */
#include "../../include/linux/mod_devicetable.h"

#define ADD(str, sep, cond, field)                              \
do {                                                            \
        strcat(str, sep);                                       \
        if (cond)                                               \
                sprintf(str + strlen(str),                      \
                        sizeof(field) == 1 ? "%02X" :           \
                        sizeof(field) == 2 ? "%04X" :           \
                        sizeof(field) == 4 ? "%08X" : "",       \
                        field);                                 \
        else                                                    \
                sprintf(str + strlen(str), "*");                \
} while(0)

/* Always end in a wildcard, for future extension */
static inline void add_wildcard(char *str)
{
	int len = strlen(str);

	if (str[len - 1] != '*')
		strcat(str + len, "*");
}

unsigned int cross_build = 0;
/**
 * Check that sizeof(device_id type) are consistent with size of section
 * in .o file. If in-consistent then userspace and kernel does not agree
 * on actual size which is a bug.
 * Also verify that the final entry in the table is all zeros.
 * Ignore both checks if build host differ from target host and size differs.
 **/
static void device_id_check(const char *modname, const char *device_id,
			    unsigned long size, unsigned long id_size,
			    void *symval)
{
	int i;

	if (size % id_size || size < id_size) {
		if (cross_build != 0)
			return;
		fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo "
		      "of the size of section __mod_%s_device_table=%lu.\n"
		      "Fix definition of struct %s_device_id "
		      "in mod_devicetable.h\n",
		      modname, device_id, id_size, device_id, size, device_id);
	}
	/* Verify last one is a terminator */
	for (i = 0; i < id_size; i++ ) {
		if (*(uint8_t*)(symval+size-id_size+i)) {
			fprintf(stderr,"%s: struct %s_device_id is %lu bytes.  "
				"The last of %lu is:\n",
				modname, device_id, id_size, size / id_size);
			for (i = 0; i < id_size; i++ )
				fprintf(stderr,"0x%02x ",
					*(uint8_t*)(symval+size-id_size+i) );
			fprintf(stderr,"\n");
			fatal("%s: struct %s_device_id is not terminated "
				"with a NULL entry!\n", modname, device_id);
		}
	}
}

/* USB is special because the bcdDevice can be matched against a numeric range */
/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */
static void do_usb_entry(struct usb_device_id *id,
			 unsigned int bcdDevice_initial, int bcdDevice_initial_digits,
			 unsigned char range_lo, unsigned char range_hi,
			 struct module *mod)
{
	char alias[500];
	strcpy(alias, "usb:");
	ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR,
	    id->idVendor);
	ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT,
	    id->idProduct);

	strcat(alias, "d");
	if (bcdDevice_initial_digits)
		sprintf(alias + strlen(alias), "%0*X",
			bcdDevice_initial_digits, bcdDevice_initial);
	if (range_lo == range_hi)
		sprintf(alias + strlen(alias), "%u", range_lo);
	else if (range_lo > 0 || range_hi < 9)
		sprintf(alias + strlen(alias), "[%u-%u]", range_lo, range_hi);
	if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
		strcat(alias, "*");

	ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS,
	    id->bDeviceClass);
	ADD(alias, "dsc",
	    id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
	    id->bDeviceSubClass);
	ADD(alias, "dp",
	    id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
	    id->bDeviceProtocol);
	ADD(alias, "ic",
	    id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS,
	    id->bInterfaceClass);
	ADD(alias, "isc",
	    id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS,
	    id->bInterfaceSubClass);
	ADD(alias, "ip",
	    id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL,
	    id->bInterfaceProtocol);

	add_wildcard(alias);
	buf_printf(&mod->dev_table_buf,
		   "MODULE_ALIAS(\"%s\");\n", alias);
}

static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
{
	unsigned int devlo, devhi;
	unsigned char chi, clo;
	int ndigits;

	id->match_flags = TO_NATIVE(id->match_flags);
	id->idVendor = TO_NATIVE(id->idVendor);
	id->idProduct = TO_NATIVE(id->idProduct);

	devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ?
		TO_NATIVE(id->bcdDevice_lo) : 0x0U;
	devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
		TO_NATIVE(id->bcdDevice_hi) : ~0x0U;

	/*
	 * Some modules (visor) have empty slots as placeholder for
	 * run-time specification that results in catch-all alias
	 */
	if (!(id->idVendor | id->idProduct | id->bDeviceClass | id->bInterfaceClass))
		return;

	/* Convert numeric bcdDevice range into fnmatch-able pattern(s) */
	for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; ndigits--) {
		clo = devlo & 0xf;
		chi = devhi & 0xf;
		if (chi > 9)	/* it's bcd not hex */
			chi = 9;
		devlo >>= 4;
		devhi >>= 4;

		if (devlo == devhi || !ndigits) {
			do_usb_entry(id, devlo, ndigits, clo, chi, mod);
			break;
		}

		if (clo > 0)
			do_usb_entry(id, devlo++, ndigits, clo, 9, mod);

		if (chi < 9)
			do_usb_entry(id, devhi--, ndigits, 0, chi, mod);
	}
}

static void do_usb_table(void *symval, unsigned long size,
			 struct module *mod)
{
	unsigned int i;
	const unsigned long id_size = sizeof(struct usb_device_id);

	device_id_check(mod->name, "usb", size, id_size, symval);

	/* Leave last one: it's the terminator. */
	size -= id_size;

	for (i = 0; i < size; i += id_size)
		do_usb_entry_multi(symval + i, mod);
}

/* Looks like: hid:bNvNpN */
static int do_hid_entry(const char *filename,
			     struct hid_device_id *id, char *alias)
{
	id->vendor = TO_NATIVE(id->vendor);
	id->product = TO_NATIVE(id->product);

	sprintf(alias, "hid:b%04X", id->bus);
	ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor);
	ADD(alias, "p", id->product != HID_ANY_ID, id->product);

	return 1;
}

/* Looks like: ieee1394:venNmoNspNverN */
static int do_ieee1394_entry(const char *filename,
			     struct ieee1394_device_id *id, char *alias)
{
	id->match_flags = TO_NATIVE(id->match_flags);
	id->vendor_id = TO_NATIVE(id->vendor_id);
	id->model_id = TO_NATIVE(id->model_id);
	id->specifier_id = TO_NATIVE(id->specifier_id);
	id->version = TO_NATIVE(id->version);

	strcpy(alias, "ieee1394:");
	ADD(alias, "ven", id->match_flags & IEEE1394_MATCH_VENDOR_ID,
	    id->vendor_id);
	ADD(alias, "mo", id->match_flags & IEEE1394_MATCH_MODEL_ID,
	    id->model_id);
	ADD(alias, "sp", id->match_flags & IEEE1394_MATCH_SPECIFIER_ID,
	    id->specifier_id);
	ADD(alias, "ver", id->match_flags & IEEE1394_MATCH_VERSION,
	    id->version);

	add_wildcard(alias);
	return 1;
}

/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
static int do_pci_entry(const char *filename,
			struct pci_device_id *id, char *alias)
{
	/* Class field can be divided into these three. */
	unsigned char baseclass, subclass, interface,
		baseclass_mask, subclass_mask, interface_mask;

	id->vendor = TO_NATIVE(id->vendor);
	id->device = TO_NATIVE(id->device);
	id->subvendor = TO_NATIVE(id->subvendor);
	id->subdevice = TO_NATIVE(id->subdevice);
	id->class = TO_NATIVE(id->class);
	id->class_mask = TO_NATIVE(id->class_mask);

	strcpy(alias, "pci:");
	ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor);
	ADD(alias, "d", id->device != PCI_ANY_ID, id->device);
	ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor);
	ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice);

	baseclass = (id->class) >> 16;
	baseclass_mask = (id->class_mask) >> 16;
	subclass = (id->class) >> 8;
	subclass_mask = (id->class_mask) >> 8;
	interface = id->class;
	interface_mask = id->class_mask;

	if ((baseclass_mask != 0 && baseclass_mask != 0xFF)
	    || (subclass_mask != 0 && subclass_mask != 0xFF)
	    || (interface_mask != 0 && interface_mask != 0xFF)) {
		warn("Can't handle masks in %s:%04X\n",
		     filename, id->class_mask);
		return 0;
	}

	ADD(alias, "bc", baseclass_mask == 0xFF, baseclass);
	ADD(alias, "sc", subclass_mask == 0xFF, subclass);
	ADD(alias, "i", interface_mask == 0xFF, interface);
	add_wildcard(alias);
	return 1;
}

/* looks like: "ccw:tNmNdtNdmN" */
static int do_ccw_entry(const char *filename,
			struct ccw_device_id *id, char *alias)
{
	id->match_flags = TO_NATIVE(id->match_flags);
	id->cu_type = TO_NATIVE(id->cu_type);
	id->cu_model = TO_NATIVE(id->cu_model);
	id->dev_type = TO_NATIVE(id->dev_type);
	id->dev_model = TO_NATIVE(id->dev_model);

	strcpy(alias, "ccw:");
	ADD(alias, "t", id->match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE,
	    id->cu_type);
	ADD(alias, "m", id->match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL,
	    id->cu_model);
	ADD(alias, "dt", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE,
	    id->dev_type);
	ADD(alias, "dm", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_MODEL,
	    id->dev_model);
	add_wildcard(alias);
	return 1;
}

/* looks like: "ap:tN" */
static int do_ap_entry(const char *filename,
		       struct ap_device_id *id, char *alias)
{
	sprintf(alias, "ap:t%02X*", id->dev_type);
	return 1;
}

/* looks like: "css:tN" */
static int do_css_entry(const char *filename,
			struct css_device_id *id, char *alias)
{
	sprintf(alias, "css:t%01X", id->type);
	return 1;
}

/* Looks like: "serio:tyNprNidNexN" */
static int do_serio_entry(const char *filename,
			  struct serio_device_id *id, char *alias)
{
	id->type = TO_NATIVE(id->type);
	id->proto = TO_NATIVE(id->proto);
	id->id = TO_NATIVE(id->id);
	id->extra = TO_NATIVE(id->extra);

	strcpy(alias, "serio:");
	ADD(alias, "ty", id->type != SERIO_ANY, id->type);
	ADD(alias, "pr", id->proto != SERIO_ANY, id->proto);
	ADD(alias, "id", id->id != SERIO_ANY, id->id);
	ADD(alias, "ex", id->extra != SERIO_ANY, id->extra);

	add_wildcard(alias);
	return 1;
}

/* looks like: "acpi:ACPI0003 or acpi:PNP0C0B" or "acpi:LNXVIDEO" */
static int do_acpi_entry(const char *filename,
			struct acpi_device_id *id, char *alias)
{
	sprintf(alias, "acpi*:%s:*", id->id);
	return 1;
}

/* looks like: "pnp:dD" */
static void do_pnp_device_entry(void *symval, unsigned long size,
				struct module *mod)
{
	const unsigned long id_size = sizeof(struct pnp_device_id);
	const unsigned int count = (size / id_size)-1;
	const struct pnp_device_id *devs = symval;
	unsigned int i;

	device_id_check(mod->name, "pnp", size, id_size, symval);

	for (i = 0; i < count; i++) {
		const char *id = (char *)devs[i].id;

		buf_printf(&mod->dev_table_buf,
			   "MODULE_ALIAS(\"pnp:d%s*\");\n", id);
		buf_printf(&mod->dev_table_buf,
			   "MODULE_ALIAS(\"acpi*:%s:*\");\n", id);
	}
}

/* looks like: "pnp:dD" for every device of the card */
static void do_pnp_card_entries(void *symval, unsigned long size,
				struct module *mod)
{
	const unsigned long id_size = sizeof(struct pnp_card_device_id);
	const unsigned int count = (size / id_size)-1;
	const struct pnp_card_device_id *cards = symval;
	unsigned int i;

	device_id_check(mod->name, "pnp", size, id_size, symval);

	for (i = 0; i < count; i++) {
		unsigned int j;
		const struct pnp_card_device_id *card = &cards[i];

		for (j = 0; j < PNP_MAX_DEVICES; j++) {
			const char *id = (char *)card->devs[j].id;
			int i2, j2;
			int dup = 0;

			if (!id[0])
				break;

			/* find duplicate, already added value */
			for (i2 = 0; i2 < i && !dup; i2++) {
				const struct pnp_card_device_id *card2 = &cards[i2];

				for (j2 = 0; j2 < PNP_MAX_DEVICES; j2++) {
					const char *id2 = (char *)card2->devs[j2].id;

					if (!id2[0])
						break;

					if (!strcmp(id, id2)) {
						dup = 1;
						break;
					}
				}
			}

			/* add an individual alias for every device entry */
			if (!dup) {
				buf_printf(&mod->dev_table_buf,
					   "MODULE_ALIAS(\"pnp:d%s*\");\n", id);
				buf_printf(&mod->dev_table_buf,
					   "MODULE_ALIAS(\"acpi*:%s:*\");\n", id);
			}
		}
	}
}

/* Looks like: pcmcia:mNcNfNfnNpfnNvaNvbNvcNvdN. */
static int do_pcmcia_entry(const char *filename,
			   struct pcmcia_device_id *id, char *alias)
{
	unsigned int i;

	id->match_flags = TO_NATIVE(id->match_flags);
	id->manf_id = TO_NATIVE(id->manf_id);
	id->card_id = TO_NATIVE(id->card_id);
	id->func_id = TO_NATIVE(id->func_id);
	id->function = TO_NATIVE(id->function);
	id->device_no = TO_NATIVE(id->device_no);

	for (i=0; i<4; i++) {
		id->prod_id_hash[i] = TO_NATIVE(id->prod_id_hash[i]);
       }

       strcpy(alias, "pcmcia:");
       ADD(alias, "m", id->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID,
	   id->manf_id);
       ADD(alias, "c", id->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID,
	   id->card_id);
       ADD(alias, "f", id->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID,
	   id->func_id);
       ADD(alias, "fn", id->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION,
	   id->function);
       ADD(alias, "pfn", id->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO,
	   id->device_no);
       ADD(alias, "pa", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1, id->prod_id_hash[0]);
       ADD(alias, "pb", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2, id->prod_id_hash[1]);
       ADD(alias, "pc", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3, id->prod_id_hash[2]);
       ADD(alias, "pd", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4, id->prod_id_hash[3]);

	add_wildcard(alias);
       return 1;
}



static int do_of_entry (const char *filename, struct of_device_id *of, char *alias)
{
    int len;
    char *tmp;
    len = sprintf (alias, "of:N%sT%s",
                    of->name[0] ? of->name : "*",
                    of->type[0] ? of->type : "*");

    if (of->compatible[0])
        sprintf (&alias[len], "%sC%s",
                     of->type[0] ? "*" : "",
                     of->compatible);

    /* Replace all whitespace with underscores */
    for (tmp = alias; tmp && *tmp; tmp++)
        if (isspace (*tmp))
            *tmp = '_';

    add_wildcard(alias);
    return 1;
}

static int do_vio_entry(const char *filename, struct vio_device_id *vio,
		char *alias)
{
	char *tmp;

	sprintf(alias, "vio:T%sS%s", vio->type[0] ? vio->type : "*",
			vio->compat[0] ? vio->compat : "*");

	/* Replace all whitespace with underscores */
	for (tmp = alias; tmp && *tmp; tmp++)
		if (isspace (*tmp))
			*tmp = '_';

	add_wildcard(alias);
	return 1;
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

static void do_input(char *alias,
		     kernel_ulong_t *arr, unsigned int min, unsigned int max)
{
	unsigned int i;

	for (i = min; i < max; i++)
		if (arr[i / BITS_PER_LONG] & (1L << (i%BITS_PER_LONG)))
			sprintf(alias + strlen(alias), "%X,*", i);
}

/* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */
static int do_input_entry(const char *filename, struct input_device_id *id,
			  char *alias)
{
	sprintf(alias, "input:");

	ADD(alias, "b", id->flags & INPUT_DEVICE_ID_MATCH_BUS, id->bustype);
	ADD(alias, "v", id->flags & INPUT_DEVICE_ID_MATCH_VENDOR, id->vendor);
	ADD(alias, "p", id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT, id->product);
	ADD(alias, "e", id->flags & INPUT_DEVICE_ID_MATCH_VERSION, id->version);

	sprintf(alias + strlen(alias), "-e*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_EVBIT)
		do_input(alias, id->evbit, 0, INPUT_DEVICE_ID_EV_MAX);
	sprintf(alias + strlen(alias), "k*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_KEYBIT)
		do_input(alias, id->keybit,
			 INPUT_DEVICE_ID_KEY_MIN_INTERESTING,
			 INPUT_DEVICE_ID_KEY_MAX);
	sprintf(alias + strlen(alias), "r*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_RELBIT)
		do_input(alias, id->relbit, 0, INPUT_DEVICE_ID_REL_MAX);
	sprintf(alias + strlen(alias), "a*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_ABSBIT)
		do_input(alias, id->absbit, 0, INPUT_DEVICE_ID_ABS_MAX);
	sprintf(alias + strlen(alias), "m*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_MSCIT)
		do_input(alias, id->mscbit, 0, INPUT_DEVICE_ID_MSC_MAX);
	sprintf(alias + strlen(alias), "l*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_LEDBIT)
		do_input(alias, id->ledbit, 0, INPUT_DEVICE_ID_LED_MAX);
	sprintf(alias + strlen(alias), "s*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_SNDBIT)
		do_input(alias, id->sndbit, 0, INPUT_DEVICE_ID_SND_MAX);
	sprintf(alias + strlen(alias), "f*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_FFBIT)
		do_input(alias, id->ffbit, 0, INPUT_DEVICE_ID_FF_MAX);
	sprintf(alias + strlen(alias), "w*");
	if (id->flags & INPUT_DEVICE_ID_MATCH_SWBIT)
		do_input(alias, id->swbit, 0, INPUT_DEVICE_ID_SW_MAX);
	return 1;
}

static int do_eisa_entry(const char *filename, struct eisa_device_id *eisa,
		char *alias)
{
	if (eisa->sig[0])
		sprintf(alias, EISA_DEVICE_MODALIAS_FMT "*", eisa->sig);
	else
		strcat(alias, "*");
	return 1;
}

/* Looks like: parisc:tNhvNrevNsvN */
static int do_parisc_entry(const char *filename, struct parisc_device_id *id,
		char *alias)
{
	id->hw_type = TO_NATIVE(id->hw_type);
	id->hversion = TO_NATIVE(id->hversion);
	id->hversion_rev = TO_NATIVE(id->hversion_rev);
	id->sversion = TO_NATIVE(id->sversion);

	strcpy(alias, "parisc:");
	ADD(alias, "t", id->hw_type != PA_HWTYPE_ANY_ID, id->hw_type);
	ADD(alias, "hv", id->hversion != PA_HVERSION_ANY_ID, id->hversion);
	ADD(alias, "rev", id->hversion_rev != PA_HVERSION_REV_ANY_ID, id->hversion_rev);
	ADD(alias, "sv", id->sversion != PA_SVERSION_ANY_ID, id->sversion);

	add_wildcard(alias);
	return 1;
}

/* Looks like: sdio:cNvNdN. */
static int do_sdio_entry(const char *filename,
			struct sdio_device_id *id, char *alias)
{
	id->class = TO_NATIVE(id->class);
	id->vendor = TO_NATIVE(id->vendor);
	id->device = TO_NATIVE(id->device);

	strcpy(alias, "sdio:");
	ADD(alias, "c", id->class != (__u8)SDIO_ANY_ID, id->class);
	ADD(alias, "v", id->vendor != (__u16)SDIO_ANY_ID, id->vendor);
	ADD(alias, "d", id->device != (__u16)SDIO_ANY_ID, id->device);
	add_wildcard(alias);
	return 1;
}

/* Looks like: ssb:vNidNrevN. */
static int do_ssb_entry(const char *filename,
			struct ssb_device_id *id, char *alias)
{
	id->vendor = TO_NATIVE(id->vendor);
	id->coreid = TO_NATIVE(id->coreid);
	id->revision = TO_NATIVE(id->revision);

	strcpy(alias, "ssb:");
	ADD(alias, "v", id->vendor != SSB_ANY_VENDOR, id->vendor);
	ADD(alias, "id", id->coreid != SSB_ANY_ID, id->coreid);
	ADD(alias, "rev", id->revision != SSB_ANY_REV, id->revision);
	add_wildcard(alias);
	return 1;
}

/* Looks like: virtio:dNvN */
static int do_virtio_entry(const char *filename, struct virtio_device_id *id,
			   char *alias)
{
	id->device = TO_NATIVE(id->device);
	id->vendor = TO_NATIVE(id->vendor);

	strcpy(alias, "virtio:");
	ADD(alias, "d", 1, id->device);
	ADD(alias, "v", id->vendor != VIRTIO_DEV_ANY_ID, id->vendor);

	add_wildcard(alias);
	return 1;
}

/* Looks like: i2c:S */
static int do_i2c_entry(const char *filename, struct i2c_device_id *id,
			char *alias)
{
	sprintf(alias, I2C_MODULE_PREFIX "%s", id->name);

	return 1;
}

static const struct dmifield {
	const char *prefix;
	int field;
} dmi_fields[] = {
	{ "bvn", DMI_BIOS_VENDOR },
	{ "bvr", DMI_BIOS_VERSION },
	{ "bd",  DMI_BIOS_DATE },
	{ "svn", DMI_SYS_VENDOR },
	{ "pn",  DMI_PRODUCT_NAME },
	{ "pvr", DMI_PRODUCT_VERSION },
	{ "rvn", DMI_BOARD_VENDOR },
	{ "rn",  DMI_BOARD_NAME },
	{ "rvr", DMI_BOARD_VERSION },
	{ "cvn", DMI_CHASSIS_VENDOR },
	{ "ct",  DMI_CHASSIS_TYPE },
	{ "cvr", DMI_CHASSIS_VERSION },
	{ NULL,  DMI_NONE }
};

static void dmi_ascii_filter(char *d, const char *s)
{
	/* Filter out characters we don't want to see in the modalias string */
	for (; *s; s++)
		if (*s > ' ' && *s < 127 && *s != ':')
			*(d++) = *s;

	*d = 0;
}


static int do_dmi_entry(const char *filename, struct dmi_system_id *id,
			char *alias)
{
	int i, j;

	sprintf(alias, "dmi*");

	for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) {
		for (j = 0; j < 4; j++) {
			if (id->matches[j].slot &&
			    id->matches[j].slot == dmi_fields[i].field) {
				sprintf(alias + strlen(alias), ":%s*",
					dmi_fields[i].prefix);
				dmi_ascii_filter(alias + strlen(alias),
						 id->matches[j].substr);
				strcat(alias, "*");
			}
		}
	}

	strcat(alias, ":");
	return 1;
}
/* Ignore any prefix, eg. some architectures prepend _ */
static inline int sym_is(const char *symbol, const char *name)
{
	const char *match;

	match = strstr(symbol, name);
	if (!match)
		return 0;
	return match[strlen(symbol)] == '\0';
}

static void do_table(void *symval, unsigned long size,
		     unsigned long id_size,
		     const char *device_id,
		     void *function,
		     struct module *mod)
{
	unsigned int i;
	char alias[500];
	int (*do_entry)(const char *, void *entry, char *alias) = function;

	device_id_check(mod->name, device_id, size, id_size, symval);
	/* Leave last one: it's the terminator. */
	size -= id_size;

	for (i = 0; i < size; i += id_size) {
		if (do_entry(mod->name, symval+i, alias)) {
			buf_printf(&mod->dev_table_buf,
				   "MODULE_ALIAS(\"%s\");\n", alias);
		}
	}
}

/* Create MODULE_ALIAS() statements.
 * At this time, we cannot write the actual output C source yet,
 * so we write into the mod->dev_table_buf buffer. */
void handle_moddevtable(struct module *mod, struct elf_info *info,
			Elf_Sym *sym, const char *symname)
{
	void *symval;
	char *zeros = NULL;

	/* We're looking for a section relative symbol */
	if (!sym->st_shndx || sym->st_shndx >= info->hdr->e_shnum)
		return;

	/* Handle all-NULL symbols allocated into .bss */
	if (info->sechdrs[sym->st_shndx].sh_type & SHT_NOBITS) {
		zeros = calloc(1, sym->st_size);
		symval = zeros;
	} else {
		symval = (void *)info->hdr
			+ info->sechdrs[sym->st_shndx].sh_offset
			+ sym->st_value;
	}

	if (sym_is(symname, "__mod_pci_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct pci_device_id), "pci",
			 do_pci_entry, mod);
	else if (sym_is(symname, "__mod_usb_device_table"))
		/* special case to handle bcdDevice ranges */
		do_usb_table(symval, sym->st_size, mod);
	else if (sym_is(symname, "__mod_hid_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct hid_device_id), "hid",
			 do_hid_entry, mod);
	else if (sym_is(symname, "__mod_ieee1394_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct ieee1394_device_id), "ieee1394",
			 do_ieee1394_entry, mod);
	else if (sym_is(symname, "__mod_ccw_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct ccw_device_id), "ccw",
			 do_ccw_entry, mod);
	else if (sym_is(symname, "__mod_ap_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct ap_device_id), "ap",
			 do_ap_entry, mod);
	else if (sym_is(symname, "__mod_css_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct css_device_id), "css",
			 do_css_entry, mod);
	else if (sym_is(symname, "__mod_serio_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct serio_device_id), "serio",
			 do_serio_entry, mod);
	else if (sym_is(symname, "__mod_acpi_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct acpi_device_id), "acpi",
			 do_acpi_entry, mod);
	else if (sym_is(symname, "__mod_pnp_device_table"))
		do_pnp_device_entry(symval, sym->st_size, mod);
	else if (sym_is(symname, "__mod_pnp_card_device_table"))
		do_pnp_card_entries(symval, sym->st_size, mod);
	else if (sym_is(symname, "__mod_pcmcia_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct pcmcia_device_id), "pcmcia",
			 do_pcmcia_entry, mod);
        else if (sym_is(symname, "__mod_of_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct of_device_id), "of",
			 do_of_entry, mod);
        else if (sym_is(symname, "__mod_vio_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct vio_device_id), "vio",
			 do_vio_entry, mod);
	else if (sym_is(symname, "__mod_input_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct input_device_id), "input",
			 do_input_entry, mod);
	else if (sym_is(symname, "__mod_eisa_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct eisa_device_id), "eisa",
			 do_eisa_entry, mod);
	else if (sym_is(symname, "__mod_parisc_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct parisc_device_id), "parisc",
			 do_parisc_entry, mod);
	else if (sym_is(symname, "__mod_sdio_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct sdio_device_id), "sdio",
			 do_sdio_entry, mod);
	else if (sym_is(symname, "__mod_ssb_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct ssb_device_id), "ssb",
			 do_ssb_entry, mod);
	else if (sym_is(symname, "__mod_virtio_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct virtio_device_id), "virtio",
			 do_virtio_entry, mod);
	else if (sym_is(symname, "__mod_i2c_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct i2c_device_id), "i2c",
			 do_i2c_entry, mod);
	else if (sym_is(symname, "__mod_dmi_device_table"))
		do_table(symval, sym->st_size,
			 sizeof(struct dmi_system_id), "dmi",
			 do_dmi_entry, mod);
	free(zeros);
}

/* Now add out buffered information to the generated C source */
void add_moddevtable(struct buffer *buf, struct module *mod)
{
	buf_printf(buf, "\n");
	buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos);
	free(mod->dev_table_buf.p);
}
ass="hl opt">->hdev; int status; temp = hub->tt.clear_list.next; clear = list_entry (temp, 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); spin_lock_irqsave (&hub->tt.lock, flags); if (status) dev_err (&hdev->dev, "clear tt %d (%04x) error %d\n", clear->tt, clear->devinfo, status); kfree(clear); } spin_unlock_irqrestore (&hub->tt.lock, flags); } /** * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub * @udev: the device whose split transaction failed * @pipe: identifies the endpoint of the failed 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. */ void usb_hub_tt_clear_buffer (struct usb_device *udev, int 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. */ if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == NULL) { dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ return; } /* info that CLEAR_TT_BUFFER needs */ clear->tt = tt->multi ? udev->ttport : 1; clear->devinfo = usb_pipeendpoint (pipe); clear->devinfo |= udev->devnum << 4; clear->devinfo |= usb_pipecontrol (pipe) ? (USB_ENDPOINT_XFER_CONTROL << 11) : (USB_ENDPOINT_XFER_BULK << 11); if (usb_pipein (pipe)) clear->devinfo |= 1 << 15; /* 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->kevent); spin_unlock_irqrestore (&tt->lock, flags); } static void hub_power_on(struct usb_hub *hub) { int port1; unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); /* 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 ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) 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->descriptor->bNbrPorts; port1++) set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); /* Wait at least 100 msec for power to become stable */ msleep(max(pgood_delay, (unsigned) 100)); } static inline void __hub_quiesce(struct usb_hub *hub) { /* (nonblocking) khubd and related activity won't re-trigger */ hub->quiescing = 1; hub->activating = 0; hub->resume_root_hub = 0; } static void hub_quiesce(struct usb_hub *hub) { /* (blocking) stop khubd and related activity */ __hub_quiesce(hub); usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work(&hub->leds); if (hub->has_indicators || hub->tt.hub) flush_scheduled_work(); } static void hub_activate(struct usb_hub *hub) { int status; hub->quiescing = 0; hub->activating = 1; hub->resume_root_hub = 0; status = usb_submit_urb(hub->urb, GFP_NOIO); if (status < 0) dev_err(hub->intfdev, "activate --> %d\n", status); if (hub->has_indicators && blinkenlights) schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); /* scan all ports ASAP */ kick_khubd(hub); } static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { int ret; ret = get_hub_status(hub->hdev, &hub->status->hub); if (ret < 0) dev_err (hub->intfdev, "%s failed (err = %d)\n", __FUNCTION__, ret); else { *status = le16_to_cpu(hub->status->hub.wHubStatus); *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; } return ret; } static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) { struct usb_device *hdev = hub->hdev; int ret; if (hdev->children[port1-1] && set_state) { usb_set_device_state(hdev->children[port1-1], USB_STATE_NOTATTACHED); } ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", port1, ret); return ret; } /* caller has locked the hub device */ static void hub_pre_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = hub->hdev; int port1; for (port1 = 1; port1 <= hdev->maxchild; ++port1) { if (hdev->children[port1 - 1]) { usb_disconnect(&hdev->children[port1 - 1]); if (hub->error == 0) hub_port_disable(hub, port1, 0); } } hub_quiesce(hub); } /* caller has locked the hub device */ static void hub_post_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); hub_activate(hub); hub_power_on(hub); } static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { struct usb_device *hdev = hub->hdev; struct device *hub_dev = hub->intfdev; u16 hubstatus, hubchange; u16 wHubCharacteristics; unsigned int pipe; int maxp, ret; char *message; hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, &hub->buffer_dma); if (!hub->buffer) { message = "can't allocate hub irq buffer"; ret = -ENOMEM; goto fail; } hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL); if (!hub->status) { message = "can't kmalloc hub status buffer"; ret = -ENOMEM; goto fail; } hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { message = "can't kmalloc hub descriptor"; ret = -ENOMEM; goto fail; } /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, * but the hub can/will return fewer bytes here. */ ret = get_hub_descriptor(hdev, hub->descriptor, sizeof(*hub->descriptor)); if (ret < 0) { message = "can't read hub descriptor"; goto fail; } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) { message = "hub has too many ports!"; ret = -ENODEV; goto fail; } hdev->maxchild = hub->descriptor->bNbrPorts; dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); if (wHubCharacteristics & HUB_CHAR_COMPOUND) { int i; char portstr [USB_MAXCHILDREN + 1]; for (i = 0; i < hdev->maxchild; i++) portstr[i] = hub->descriptor->DeviceRemovable [((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; portstr[hdev->maxchild] = 0; dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr); } else dev_dbg(hub_dev, "standalone hub\n"); switch (wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: dev_dbg(hub_dev, "ganged power switching\n"); break; case 0x01: dev_dbg(hub_dev, "individual port power switching\n"); break; case 0x02: case 0x03: dev_dbg(hub_dev, "no power switching (usb 1.0)\n"); break; } switch (wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: dev_dbg(hub_dev, "global over-current protection\n"); break; case 0x08: dev_dbg(hub_dev, "individual port over-current protection\n"); break; case 0x10: case 0x18: dev_dbg(hub_dev, "no over-current protection\n"); break; } spin_lock_init (&hub->tt.lock); INIT_LIST_HEAD (&hub->tt.clear_list); INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub); switch (hdev->descriptor.bDeviceProtocol) { case 0: break; case 1: dev_dbg(hub_dev, "Single TT\n"); hub->tt.hub = hdev; break; case 2: ret = usb_set_interface(hdev, 0, 1); if (ret == 0) { dev_dbg(hub_dev, "TT per port\n"); hub->tt.multi = 1; } else dev_err(hub_dev, "Using single TT (err %d)\n", ret); hub->tt.hub = hdev; break; default: dev_dbg(hub_dev, "Unrecognized hub protocol %d\n", hdev->descriptor.bDeviceProtocol); break; } /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */ switch (wHubCharacteristics & HUB_CHAR_TTTT) { case HUB_TTTT_8_BITS: if (hdev->descriptor.bDeviceProtocol != 0) { hub->tt.think_time = 666; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 8, hub->tt.think_time); } break; case HUB_TTTT_16_BITS: hub->tt.think_time = 666 * 2; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 16, hub->tt.think_time); break; case HUB_TTTT_24_BITS: hub->tt.think_time = 666 * 3; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 24, hub->tt.think_time); break; case HUB_TTTT_32_BITS: hub->tt.think_time = 666 * 4; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 32, hub->tt.think_time); break; } /* probe() zeroes hub->indicator[] */ if (wHubCharacteristics & HUB_CHAR_PORTIND) { hub->has_indicators = 1; dev_dbg(hub_dev, "Port indicators are supported\n"); } dev_dbg(hub_dev, "power on to power good time: %dms\n", hub->descriptor->bPwrOn2PwrGood * 2); /* power budgeting mostly matters with bus-powered hubs, * and battery-powered root hubs (may provide just 8 mA). */ ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); if (ret < 2) { message = "can't get hub status"; goto fail; } le16_to_cpus(&hubstatus); if (hdev == hdev->bus->root_hub) { if (hdev->bus_mA == 0 || hdev->bus_mA >= 500) hub->mA_per_port = 500; else { hub->mA_per_port = hdev->bus_mA; hub->limited_power = 1; } } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", hub->descriptor->bHubContrCurrent); hub->limited_power = 1; if (hdev->maxchild > 0) { int remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent; if (remaining < hdev->maxchild * 100) dev_warn(hub_dev, "insufficient power available " "to use all downstream ports\n"); hub->mA_per_port = 100; /* 7.2.1.1 */ } } else { /* Self-powered external hub */ /* FIXME: What about battery-powered external hubs that * provide less current per port? */ hub->mA_per_port = 500; } if (hub->mA_per_port < 500) dev_dbg(hub_dev, "%umA bus power budget for each child\n", hub->mA_per_port); ret = hub_hub_status(hub, &hubstatus, &hubchange); if (ret < 0) { message = "can't get hub status"; goto fail; } /* local power status reports aren't always correct */ if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER) dev_dbg(hub_dev, "local power source is %s\n", (hubstatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0) dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); /* set up the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); if (maxp > sizeof(*hub->buffer)) maxp = sizeof(*hub->buffer); hub->urb = usb_alloc_urb(0, GFP_KERNEL); if (!hub->urb) { message = "couldn't allocate interrupt urb"; ret = -ENOMEM; goto fail; } usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); hub->urb->transfer_dma = hub->buffer_dma; hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* maybe cycle the hub leds */ if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; hub_power_on(hub); hub_activate(hub); return 0; fail: dev_err (hub_dev, "config failed, %s (err %d)\n", message, ret); /* hub_disconnect() frees urb and descriptor */ return ret; } static unsigned highspeed_hubs; static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev; /* Disconnect all children and quiesce the hub */ hub->error = 0; hub_pre_reset(intf); usb_set_intfdata (intf, NULL); hdev = hub->hdev; if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; usb_free_urb(hub->urb); hub->urb = NULL; spin_lock_irq(&hub_event_lock); list_del_init(&hub->event_list); spin_unlock_irq(&hub_event_lock); kfree(hub->descriptor); hub->descriptor = NULL; kfree(hub->status); hub->status = NULL; if (hub->buffer) { usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer, hub->buffer_dma); hub->buffer = NULL; } kfree(hub); } static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_host_interface *desc; struct usb_endpoint_descriptor *endpoint; struct usb_device *hdev; struct usb_hub *hub; desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); #ifdef CONFIG_USB_OTG_BLACKLIST_HUB if (hdev->parent) { dev_warn(&intf->dev, "ignoring external hub\n"); return -ENODEV; } #endif /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((desc->desc.bInterfaceSubClass != 0) && (desc->desc.bInterfaceSubClass != 1)) { descriptor_error: dev_err (&intf->dev, "bad descriptor, ignoring hub\n"); return -EIO; } /* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (desc->desc.bNumEndpoints != 1) goto descriptor_error; endpoint = &desc->endpoint[0].desc; /* Output endpoint? Curiouser and curiouser.. */ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) goto descriptor_error; /* If it's not an interrupt endpoint, we'd better punt! */ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) goto descriptor_error; /* We found a hub */ dev_info (&intf->dev, "USB hub found\n"); hub = kzalloc(sizeof(*hub), GFP_KERNEL); if (!hub) { dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n"); return -ENOMEM; } INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_WORK(&hub->leds, led_work, hub); usb_set_intfdata (intf, hub); if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs++; if (hub_configure(hub, endpoint) >= 0) return 0; hub_disconnect (intf); return -ENODEV; } static int hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) { struct usb_device *hdev = interface_to_usbdev (intf); /* assert ifno == 0 (part of hub spec) */ switch (code) { case USBDEVFS_HUB_PORTINFO: { struct usbdevfs_hub_portinfo *info = user_data; int i; spin_lock_irq(&device_state_lock); if (hdev->devnum <= 0) info->nports = 0; else { info->nports = hdev->maxchild; for (i = 0; i < info->nports; i++) { if (hdev->children[i] == NULL) info->port[i] = 0; else info->port[i] = hdev->children[i]->devnum; } } spin_unlock_irq(&device_state_lock); return info->nports + 1; } default: return -ENOSYS; } } /* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent * tree operations like suspend, resume, reset, and disconnect, which * apply to everything downstream of a given port. */ static int locktree(struct usb_device *udev) { int t; struct usb_device *hdev; if (!udev) return -ENODEV; /* root hub is always the first lock in the series */ hdev = udev->parent; if (!hdev) { usb_lock_device(udev); return 0; } /* on the path from root to us, lock everything from * top down, dropping parent locks when not needed */ t = locktree(hdev); if (t < 0) return t; /* everything is fail-fast once disconnect * processing starts */ if (udev->state == USB_STATE_NOTATTACHED) { usb_unlock_device(hdev); return -ENODEV; } /* when everyone grabs locks top->bottom, * non-overlapping work may be concurrent */ usb_lock_device(udev); usb_unlock_device(hdev); return udev->portnum; } static void recursively_mark_NOTATTACHED(struct usb_device *udev) { int i; for (i = 0; i < udev->maxchild; ++i) { if (udev->children[i]) recursively_mark_NOTATTACHED(udev->children[i]); } udev->state = USB_STATE_NOTATTACHED; } /** * usb_set_device_state - change a device's current state (usbcore, hcds) * @udev: pointer to device whose state should be changed * @new_state: new state value to be stored * * udev->state is _not_ fully protected by the device lock. Although * most transitions are made only while holding the lock, the state can * can change to USB_STATE_NOTATTACHED at almost any time. This * is so that devices can be marked as disconnected as soon as possible, * without having to wait for any semaphores to be released. As a result, * all changes to any device's state must be protected by the * device_state_lock spinlock. * * Once a device has been added to the device tree, all changes to its state * should be made using this routine. The state should _not_ be set directly. * * If udev->state is already USB_STATE_NOTATTACHED then no change is made. * Otherwise udev->state is set to new_state, and if new_state is * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set * to USB_STATE_NOTATTACHED. */ void usb_set_device_state(struct usb_device *udev, enum usb_device_state new_state) { unsigned long flags; spin_lock_irqsave(&device_state_lock, flags); if (udev->state == USB_STATE_NOTATTACHED) ; /* do nothing */ else if (new_state != USB_STATE_NOTATTACHED) { udev->state = new_state; /* root hub wakeup capabilities are managed out-of-band * and may involve silicon errata ... ignore them here. */ if (udev->parent) { if (new_state == USB_STATE_CONFIGURED) device_init_wakeup(&udev->dev, (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP)); else if (new_state != USB_STATE_SUSPENDED) device_init_wakeup(&udev->dev, 0); } } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); } #ifdef CONFIG_PM /** * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power * @rhdev: struct usb_device for the root hub * * The USB host controller driver calls this function when its root hub * is resumed and Vbus power has been interrupted or the controller * has been reset. The routine marks all the children of the root hub * as NOTATTACHED and marks logical connect-change events on their ports. */ void usb_root_hub_lost_power(struct usb_device *rhdev) { struct usb_hub *hub; int port1; unsigned long flags; dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); spin_lock_irqsave(&device_state_lock, flags); hub = hdev_to_hub(rhdev); for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { if (rhdev->children[port1 - 1]) { recursively_mark_NOTATTACHED( rhdev->children[port1 - 1]); set_bit(port1, hub->change_bits); } } spin_unlock_irqrestore(&device_state_lock, flags); } EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); #endif static void choose_address(struct usb_device *udev) { int devnum; struct usb_bus *bus = udev->bus; /* If khubd ever becomes multithreaded, this will need a lock */ /* Try to allocate the next devnum beginning at bus->devnum_next. */ devnum = find_next_zero_bit(bus->devmap.devicemap, 128, bus->devnum_next); if (devnum >= 128) devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1); bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); if (devnum < 128) { set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } } static void release_address(struct usb_device *udev) { if (udev->devnum > 0) { clear_bit(udev->devnum, udev->bus->devmap.devicemap); udev->devnum = -1; } } /** * usb_disconnect - disconnect a device (usbcore-internal) * @pdev: pointer to device being disconnected * Context: !in_interrupt () * * Something got disconnected. Get rid of it and all of its children. * * If *pdev is a normal device then the parent hub must already be locked. * If *pdev is a root hub then this routine will acquire the * usb_bus_list_lock on behalf of the caller. * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. * * This call is synchronous, and may not be used in an interrupt context. */ void usb_disconnect(struct usb_device **pdev) { struct usb_device *udev = *pdev; int i; if (!udev) { pr_debug ("%s nodev\n", __FUNCTION__); return; } /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. * this quiesces everyting except pending urbs. */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); usb_lock_device(udev); /* Free up all the children before we remove this device */ for (i = 0; i < USB_MAXCHILDREN; i++) { if (udev->children[i]) usb_disconnect(&udev->children[i]); } /* deallocate hcd/hardware state ... nuking all pending urbs and * cleaning up all state associated with the current configuration * so that the hardware is now fully quiesced. */ usb_disable_device(udev, 0); usb_notify_remove_device(udev); /* Free the device number, remove the /proc/bus/usb entry and * the sysfs attributes, and delete the parent's children[] * (or root_hub) pointer. */ dev_dbg (&udev->dev, "unregistering device\n"); release_address(udev); usb_remove_sysfs_dev_files(udev); /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); *pdev = NULL; spin_unlock_irq(&device_state_lock); usb_unlock_device(udev); device_unregister(&udev->dev); } static inline const char *plural(int n) { return (n == 1 ? "" : "s"); } static int choose_configuration(struct usb_device *udev) { int i; int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; best = NULL; c = udev->config; num_configs = udev->descriptor.bNumConfigurations; for (i = 0; i < num_configs; (i++, c++)) { struct usb_interface_descriptor *desc = NULL; /* It's possible that a config has no interfaces! */ if (c->desc.bNumInterfaces > 0) desc = &c->intf_cache[0]->altsetting->desc; /* * HP's USB bus-powered keyboard has only one configuration * and it claims to be self-powered; other devices may have * similar errors in their descriptors. If the next test * were allowed to execute, such configurations would always * be rejected and the devices would not work as expected. * In the meantime, we run the risk of selecting a config * that requires external power at a time when that power * isn't available. It seems to be the lesser of two evils. * * Bugzilla #6448 reports a device that appears to crash * when it receives a GET_DEVICE_STATUS request! We don't * have any other way to tell whether a device is self-powered, * but since we don't use that information anywhere but here, * the call has been removed. * * Maybe the GET_DEVICE_STATUS call and the test below can * be reinstated when device firmwares become more reliable. * Don't hold your breath. */ #if 0 /* Rule out self-powered configs for a bus-powered device */ if (bus_powered && (c->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)) continue; #endif /* * The next test may not be as effective as it should be. * Some hubs have errors in their descriptor, claiming * to be self-powered when they are really bus-powered. * We will overestimate the amount of current such hubs * make available for each port. * * This is a fairly benign sort of failure. It won't * cause us to reject configurations that we should have * accepted. */ /* Rule out configs that draw too much bus current */ if (c->desc.bMaxPower * 2 > udev->bus_mA) { insufficient_power++; continue; } /* If the first config's first interface is COMM/2/0xff * (MSFT RNDIS), rule it out unless Linux has host-side * RNDIS support. */ if (i == 0 && desc && desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 && desc->bInterfaceProtocol == 0xff) { #ifndef CONFIG_USB_NET_RNDIS_HOST continue; #else best = c; #endif } /* From the remaining configs, choose the first one whose * first interface is for a non-vendor-specific class. * Reason: Linux is more likely to have a class driver * than a vendor-specific driver. */ else if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC && (!desc || desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC)) { best = c; break; } /* If all the remaining configs are vendor-specific, * choose the first one. */ else if (!best) best = c; } if (insufficient_power > 0) dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", insufficient_power, plural(insufficient_power)); if (best) { i = best->desc.bConfigurationValue; dev_info(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); } else { i = -1; dev_warn(&udev->dev, "no configuration chosen from %d choice%s\n", num_configs, plural(num_configs)); } return i; } #ifdef DEBUG static void show_string(struct usb_device *udev, char *id, char *string) { if (!string) return; dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string); } #else static inline void show_string(struct usb_device *udev, char *id, char *string) {} #endif #ifdef CONFIG_USB_OTG #include "otg_whitelist.h" #endif /** * usb_new_device - perform initial device setup (usbcore-internal) * @udev: newly addressed device (in ADDRESS state) * * This is called with devices which have been enumerated, but not yet * configured. The device descriptor is available, but not descriptors * for any device configuration. The caller must have locked either * the parent hub (if udev is a normal device) or else the * usb_bus_list_lock (if udev is a root hub). The parent's pointer to * udev has already been installed, but udev is not yet visible through * sysfs or other filesystem code. * * Returns 0 for success (device is configured and listed, with its * interfaces, in sysfs); else a negative errno value. * * This call is synchronous, and may not be used in an interrupt context. * * Only the hub driver or root-hub registrar should ever call this. */ int usb_new_device(struct usb_device *udev) { int err; int c; err = usb_get_configuration(udev); if (err < 0) { dev_err(&udev->dev, "can't read configurations, error %d\n", err); goto fail; } /* read the standard strings and cache them if present */ udev->product = usb_cache_string(udev, udev->descriptor.iProduct); udev->manufacturer = usb_cache_string(udev, udev->descriptor.iManufacturer); udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); /* Tell the world! */ dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, " "SerialNumber=%d\n", udev->descriptor.iManufacturer, udev->descriptor.iProduct, udev->descriptor.iSerialNumber); show_string(udev, "Product", udev->product); show_string(udev, "Manufacturer", udev->manufacturer); show_string(udev, "SerialNumber", udev->serial); #ifdef CONFIG_USB_OTG /* * OTG-aware devices on OTG-capable root hubs may be able to use SRP, * to wake us after we've powered off VBUS; and HNP, switching roles * "host" to "peripheral". The OTG descriptor helps figure this out. */ if (!udev->bus->is_b_host && udev->config && udev->parent == udev->bus->root_hub) { struct usb_otg_descriptor *desc = 0; struct usb_bus *bus = udev->bus; /* descriptor may appear anywhere in config */ if (__usb_get_extra_descriptor (udev->rawdescriptors[0], le16_to_cpu(udev->config[0].desc.wTotalLength), USB_DT_OTG, (void **) &desc) == 0) { if (desc->bmAttributes & USB_OTG_HNP) { unsigned port1 = udev->portnum; struct usb_device *root = udev->parent; dev_info(&udev->dev, "Dual-Role OTG device on %sHNP port\n", (port1 == bus->otg_port) ? "" : "non-"); /* enable HNP before suspend, it's simpler */ if (port1 == bus->otg_port) bus->b_hnp_enable = 1; err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, 0, bus->b_hnp_enable ? USB_DEVICE_B_HNP_ENABLE : USB_DEVICE_A_ALT_HNP_SUPPORT, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (err < 0) { /* OTG MESSAGE: report errors here, * customize to match your product. */ dev_info(&udev->dev, "can't set HNP mode; %d\n", err); bus->b_hnp_enable = 0; } } } } if (!is_targeted(udev)) { /* Maybe it can talk to us, though we can't talk to it. * (Includes HNP test device.) */ if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { static int __usb_suspend_device(struct usb_device *, int port1); err = __usb_suspend_device(udev, udev->bus->otg_port); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } err = -ENODEV; goto fail; } #endif /* put device-specific files into sysfs */ err = device_add (&udev->dev); if (err) { dev_err(&udev->dev, "can't device_add, error %d\n", err); goto fail; } usb_create_sysfs_dev_files (udev); usb_lock_device(udev); /* choose and set the configuration. that registers the interfaces * with the driver core, and lets usb device drivers bind to them. */ c = choose_configuration(udev); if (c >= 0) { err = usb_set_configuration(udev, c); if (err) { dev_err(&udev->dev, "can't set config #%d, error %d\n", c, err); /* This need not be fatal. The user can try to * set other configurations. */ } } /* USB device state == configured ... usable */ usb_notify_add_device(udev); usb_unlock_device(udev); return 0; fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); return err; } static int hub_port_status(struct usb_hub *hub, int port1, u16 *status, u16 *change) { int ret; ret = get_port_status(hub->hdev, port1, &hub->status->port); if (ret < 0) dev_err (hub->intfdev, "%s failed (err = %d)\n", __FUNCTION__, ret); else { *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); ret = 0; } return ret; } #define PORT_RESET_TRIES 5 #define SET_ADDRESS_TRIES 2 #define GET_DESCRIPTOR_TRIES 2 #define SET_CONFIG_TRIES (2 * (use_both_schemes + 1)) #define USE_NEW_SCHEME(i) ((i) / 2 == old_scheme_first) #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 static int hub_port_wait_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay) { int delay_time, ret; u16 portstatus; u16 portchange; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { /* wait to give the device a chance to reset */ msleep(delay); /* read and decode port status */ ret = hub_port_status(hub, port1, &portstatus, &portchange); if (ret < 0) return ret; /* Device went away? */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; /* bomb out completely if something weird happened */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -EINVAL; /* if we`ve finished resetting, then break out of the loop */ if (!(portstatus & USB_PORT_STAT_RESET) && (portstatus & USB_PORT_STAT_ENABLE)) { if (portstatus & USB_PORT_STAT_HIGH_SPEED) udev->speed = USB_SPEED_HIGH; else if (portstatus & USB_PORT_STAT_LOW_SPEED) udev->speed = USB_SPEED_LOW; else udev->speed = USB_SPEED_FULL; return 0; } /* switch to the long delay after two short delay failures */ if (delay_time >= 2 * HUB_SHORT_RESET_TIME) delay = HUB_LONG_RESET_TIME; dev_dbg (hub->intfdev, "port %d not reset yet, waiting %dms\n", port1, delay); } return -EBUSY; } static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay) { int i, status; /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_RESET); if (status) dev_err(hub->intfdev, "cannot reset port %d (err = %d)\n", port1, status); else { status = hub_port_wait_reset(hub, port1, udev, delay); if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", status); } /* return on disconnect or reset */ switch (status) { case 0: /* TRSTRCY = 10 ms; plus some extra */ msleep(10 + 40); /* FALL THROUGH */ case -ENOTCONN: case -ENODEV: clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_RESET); /* FIXME need disconnect() for NOTATTACHED device */ usb_set_device_state(udev, status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT); return status; } dev_dbg (hub->intfdev, "port %d not enabled, trying reset again...\n", port1); delay = HUB_LONG_RESET_TIME; } dev_err (hub->intfdev, "Cannot enable port %i. Maybe the USB cable is bad?\n", port1); return status; } /* * Disable a port and mark a logical connnect-change event, so that some * time later khubd 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->intfdev, "logical disconnect on port %d\n", port1); 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 * khubd reactivates the port later (timer, SRP, etc). * Powerdown must be optional, because of reset/DFU. */ set_bit(port1, hub->change_bits); kick_khubd(hub); } #ifdef CONFIG_USB_SUSPEND /* * Selective port suspend reduces power; most suspended devices draw * less than 500 uA. It's also used in OTG, along with remote wakeup. * All devices below the suspended port are also suspended. * * Devices leave suspend state when the host wakes them up. Some devices * also support "remote wakeup", where the device can activate the USB * tree above them to deliver data, such as a keypress or packet. In * some cases, this wakes the USB host. */ static int hub_port_suspend(struct usb_hub *hub, int port1, struct usb_device *udev) { int status; // dev_dbg(hub->intfdev, "suspend port %d\n", port1); /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). * * NOTE: OTG devices may issue remote wakeup (or SRP) even when * we don't explicitly enable it here. */ if (device_may_wakeup(&udev->dev)) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); } /* see 7.1.7.6 */ status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } else { /* device has up to 10 msec to fully suspend */ dev_dbg(&udev->dev, "usb suspend\n"); usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } return status; } /* * Devices on USB hub ports have only one "suspend" state, corresponding * to ACPI D2, "may cause the device to lose some context". * State transitions include: * * - suspend, resume ... when the VBUS power link stays live * - suspend, disconnect ... VBUS lost * * Once VBUS drop breaks the circuit, the port it's using has to go through * normal re-enumeration procedures, starting with enabling VBUS power. * Other than re-initializing the hub (plug/unplug, except for root hubs), * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. * * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when * the root hub for their bus goes into global suspend ... so we don't * (falsely) update the device power state to say it suspended. */ static int __usb_suspend_device (struct usb_device *udev, int port1) { int status = 0; /* caller owns the udev device lock */ if (port1 < 0) return port1; if (udev->state == USB_STATE_SUSPENDED || udev->state == USB_STATE_NOTATTACHED) { return 0; } /* all interfaces must already be suspended */ if (udev->actconfig) { int i; for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *intf; intf = udev->actconfig->interface[i]; if (is_active(intf)) { dev_dbg(&intf->dev, "nyet suspended\n"); return -EBUSY; } } } /* we only change a device's upstream USB link. * root hubs have no upstream USB link. */ if (udev->parent) status = hub_port_suspend(hdev_to_hub(udev->parent), port1, udev); if (status == 0) udev->dev.power.power_state = PMSG_SUSPEND; return status; } #endif /* * usb_suspend_device - suspend a usb device * @udev: device that's no longer in active use * Context: must be able to sleep; device not locked; pm locks held * * Suspends a USB device that isn't in active use, conserving power. * Devices may wake out of a suspend, if anything important happens, * using the remote wakeup mechanism. They may also be taken out of * suspend by the host, using usb_resume_device(). It's also routine * to disconnect devices while they are suspended. * * This only affects the USB hardware for a device; its interfaces * (and, for hubs, child devices) must already have been suspended. * * Suspending OTG devices may trigger HNP, if that's been enabled * between a pair of dual-role devices. That will change roles, such * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. * * Returns 0 on success, else negative errno. */ int usb_suspend_device(struct usb_device *udev) { #ifdef CONFIG_USB_SUSPEND if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; return __usb_suspend_device(udev, udev->portnum); #else /* NOTE: udev->state unchanged, it's not lying ... */ udev->dev.power.power_state = PMSG_SUSPEND; return 0; #endif } /* * If the USB "suspend" state is in use (rather than "global suspend"), * many devices will be individually taken out of suspend state using * special" resume" signaling. These routines kick in shortly after * hardware resume signaling is finished, either because of selective * resume (by host) or remote wakeup (by device) ... now see what changed * in the tree that's rooted at this device. */ static int finish_device_resume(struct usb_device *udev) { int status; u16 devstatus; /* caller owns the udev device lock */ dev_dbg(&udev->dev, "finish resume\n"); /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the * first two on the host side; they'd be inside hub_port_init() * during many timeouts, but khubd can't suspend until later. */ usb_set_device_state(udev, udev->actconfig ? USB_STATE_CONFIGURED : USB_STATE_ADDRESS); udev->dev.power.power_state = PMSG_ON; /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, * and device drivers will know about any resume quirks. */ status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); if (status < 2) dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); else if (udev->actconfig) { unsigned i; int (*resume)(struct device *); le16_to_cpus(&devstatus); if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) && udev->parent) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) { dev_dbg(&udev->dev, "disable remote " "wakeup, status %d\n", status); status = 0; } } /* resume interface drivers; if this is a hub, it * may have a child resume event to deal with soon */ resume = udev->dev.bus->resume; for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { struct device *dev = &udev->actconfig->interface[i]->dev; down(&dev->sem); (void) resume(dev); up(&dev->sem); } status = 0; } else if (udev->devnum <= 0) { dev_dbg(&udev->dev, "bogus resume!\n"); status = -EINVAL; } return status; } #ifdef CONFIG_USB_SUSPEND static int hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) { int status; // dev_dbg(hub->intfdev, "resume port %d\n", port1); /* see 7.1.7.7; affects power usage, but not budgeting */ status = clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", port1, status); } else { u16 devstatus; u16 portchange; /* drive resume for at least 20 msec */ if (udev) dev_dbg(&udev->dev, "RESUME\n"); msleep(25); #define LIVE_FLAGS ( USB_PORT_STAT_POWER \ | USB_PORT_STAT_ENABLE \ | USB_PORT_STAT_CONNECTION) /* Virtual root hubs can trigger on GET_PORT_STATUS to * stop resume signaling. Then finish the resume * sequence. */ devstatus = portchange = 0; status = hub_port_status(hub, port1, &devstatus, &portchange); if (status < 0 || (devstatus & LIVE_FLAGS) != LIVE_FLAGS || (devstatus & USB_PORT_STAT_SUSPEND) != 0 ) { dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", port1, portchange, devstatus, status); } else { /* TRSMRCY = 10 msec */ msleep(10); if (udev) status = finish_device_resume(udev); } } if (status < 0) hub_port_logical_disconnect(hub, port1); return status; } #endif /* * usb_resume_device - re-activate a suspended usb device * @udev: device to re-activate * Context: must be able to sleep; device not locked; pm locks held * * This will re-activate the suspended device, increasing power usage * while letting drivers communicate again with its endpoints. * USB resume explicitly guarantees that the power session between * the host and the device is the same as it was when the device * suspended. * * Returns 0 on success, else negative errno. */ int usb_resume_device(struct usb_device *udev) { int status; if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; /* selective resume of one downstream hub-to-device port */ if (udev->parent) { #ifdef CONFIG_USB_SUSPEND if (udev->state == USB_STATE_SUSPENDED) { // NOTE swsusp may bork us, device state being wrong... // NOTE this fails if parent is also suspended... status = hub_port_resume(hdev_to_hub(udev->parent), udev->portnum, udev); } else #endif status = 0; } else status = finish_device_resume(udev); if (status < 0) dev_dbg(&udev->dev, "can't resume, status %d\n", status); /* rebind drivers that had no suspend() */ if (status == 0) { usb_unlock_device(udev); bus_rescan_devices(&usb_bus_type); usb_lock_device(udev); } return status; } static int remote_wakeup(struct usb_device *udev) { int status = 0; #ifdef CONFIG_USB_SUSPEND /* don't repeat RESUME sequence if this device * was already woken up by some other task */ usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "RESUME (wakeup)\n"); /* TRSMRCY = 10 msec */ msleep(10); status = finish_device_resume(udev); } usb_unlock_device(udev); #endif return status; } static int hub_suspend(struct usb_interface *intf, pm_message_t msg) { struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; udev = hdev->children [port1-1]; if (udev && (udev->dev.power.power_state.event == PM_EVENT_ON #ifdef CONFIG_USB_SUSPEND || udev->state != USB_STATE_SUSPENDED #endif )) { dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); return -EBUSY; } } /* "global suspend" of the downstream HC-to-USB interface */ if (!hdev->parent) { struct usb_bus *bus = hdev->bus; if (bus) { int status = hcd_bus_suspend (bus); if (status != 0) { dev_dbg(&hdev->dev, "'global' suspend %d\n", status); return status; } } else return -EOPNOTSUPP; } /* stop khubd and related activity */ hub_quiesce(hub); return 0; } static int hub_resume(struct usb_interface *intf) { struct usb_device *hdev = interface_to_usbdev(intf); struct usb_hub *hub = usb_get_intfdata (intf); int status; /* "global resume" of the downstream HC-to-USB interface */ if (!hdev->parent) { struct usb_bus *bus = hdev->bus; if (bus) { status = hcd_bus_resume (bus); if (status) { dev_dbg(&intf->dev, "'global' resume %d\n", status); return status; } } else return -EOPNOTSUPP; if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); } } hub_activate(hub); /* REVISIT: this recursion probably shouldn't exist. Remove * this code sometime, after retesting with different root and * external hubs. */ #ifdef CONFIG_USB_SUSPEND { unsigned port1; for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; u16 portstat, portchange; udev = hdev->children [port1-1]; status = hub_port_status(hub, port1, &portstat, &portchange); if (status == 0) { if (portchange & USB_PORT_STAT_C_SUSPEND) { clear_port_feature(hdev, port1, USB_PORT_FEAT_C_SUSPEND); portchange &= ~USB_PORT_STAT_C_SUSPEND; } /* let khubd handle disconnects etc */ if (portchange) continue; } if (!udev || status < 0) continue; usb_lock_device(udev); if (portstat & USB_PORT_STAT_SUSPEND) status = hub_port_resume(hub, port1, udev); else { status = finish_device_resume(udev); if (status < 0) { dev_dbg(&intf->dev, "resume port %d --> %d\n", port1, status); hub_port_logical_disconnect(hub, port1); } } usb_unlock_device(udev); } } #endif return 0; } void usb_suspend_root_hub(struct usb_device *hdev) { struct usb_hub *hub = hdev_to_hub(hdev); /* This also makes any led blinker stop retriggering. We're called * from irq, so the blinker might still be scheduled. Caller promises * that the root hub status URB will be canceled. */ __hub_quiesce(hub); mark_quiesced(to_usb_interface(hub->intfdev)); } void usb_resume_root_hub(struct usb_device *hdev) { struct usb_hub *hub = hdev_to_hub(hdev); hub->resume_root_hub = 1; kick_khubd(hub); } /* USB 2.0 spec, 7.1.7.3 / fig 7-29: * * Between connect detection and reset signaling there must be a delay * of 100ms at least for debounce and power-settling. The corresponding * timer shall restart whenever the downstream port detects a disconnect. * * Apparently there are some bluetooth and irda-dongles and a number of * low-speed devices for which this debounce period may last over a second. * Not covered by the spec - but easy to deal with. * * This implementation uses a 1500ms total debounce timeout; if the * connection isn't stable by then it returns -ETIMEDOUT. It checks * every 25ms for transient disconnects. When the port status has been * unchanged for 100ms it returns the port status. */ #define HUB_DEBOUNCE_TIMEOUT 1500 #define HUB_DEBOUNCE_STEP 25 #define HUB_DEBOUNCE_STABLE 100 static int hub_port_debounce(struct usb_hub *hub, int port1) { int ret; int total_time, stable_time = 0; u16 portchange, portstatus; unsigned connection = 0xffff; for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { ret = hub_port_status(hub, port1, &portstatus, &portchange); if (ret < 0) return ret; if (!(portchange & USB_PORT_STAT_C_CONNECTION) && (portstatus & USB_PORT_STAT_CONNECTION) == connection) { stable_time += HUB_DEBOUNCE_STEP; if (stable_time >= HUB_DEBOUNCE_STABLE) break; } else { stable_time = 0; connection = portstatus & USB_PORT_STAT_CONNECTION; } if (portchange & USB_PORT_STAT_C_CONNECTION) { clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_CONNECTION); } if (total_time >= HUB_DEBOUNCE_TIMEOUT) break; msleep(HUB_DEBOUNCE_STEP); } dev_dbg (hub->intfdev, "debounce: port %d: total %dms stable %dms status 0x%x\n", port1, total_time, stable_time, portstatus); if (stable_time < HUB_DEBOUNCE_STABLE) return -ETIMEDOUT; return portstatus; } static void ep0_reinit(struct usb_device *udev) { usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_OUT); udev->ep_in[0] = udev->ep_out[0] = &udev->ep0; } #define usb_sndaddr0pipe() (PIPE_CONTROL << 30) #define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN) static int hub_set_address(struct usb_device *udev) { int retval; if (udev->devnum == 0) return -EINVAL; if (udev->state == USB_STATE_ADDRESS) return 0; if (udev->state != USB_STATE_DEFAULT) return -EINVAL; retval = usb_control_msg(udev, usb_sndaddr0pipe(), USB_REQ_SET_ADDRESS, 0, udev->devnum, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval == 0) { usb_set_device_state(udev, USB_STATE_ADDRESS); ep0_reinit(udev); } return retval; } /* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of * usb_reset_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */ static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter) { static DEFINE_MUTEX(usb_address0_mutex); struct usb_device *hdev = hub->hdev; int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; /* root hub ports have a slightly longer reset period * (from USB 2.0 spec, section 7.1.7.5) */ if (!hdev->parent) { delay = HUB_ROOT_RESET_TIME; if (port1 == hdev->bus->otg_port) hdev->bus->b_hnp_enable = 0; } /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; mutex_lock(&usb_address0_mutex); /* Reset the device; full speed may morph to high speed */ retval = hub_port_reset(hub, port1, udev, delay); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ retval = -ENODEV; if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { dev_dbg(&udev->dev, "device reset changed speed!\n"); goto fail; } oldspeed = udev->speed; /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. */ switch (udev->speed) { case USB_SPEED_HIGH: /* fixed at 64 */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); break; case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ /* to determine the ep0 maxpacket size, try to read * the device descriptor to get bMaxPacketSize0 and * then correct our initial guess. */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); break; case USB_SPEED_LOW: /* fixed at 8 */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8); break; default: goto fail; } dev_info (&udev->dev, "%s %s speed USB device using %s and address %d\n", (udev->config) ? "reset" : "new", ({ char *speed; switch (udev->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break; }; speed;}), udev->bus->controller->driver->name, udev->devnum); /* Set up TT records, if needed */ if (hdev->tt) { udev->tt = hdev->tt; udev->ttport = hdev->ttport; } else if (udev->speed != USB_SPEED_HIGH && hdev->speed == USB_SPEED_HIGH) { udev->tt = &hub->tt; udev->ttport = port1; } /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? * Because device hardware and firmware is sometimes buggy in * this area, and this is how Linux has done it for ages. * Change it cautiously. * * NOTE: If USE_NEW_SCHEME() is true we will start by issuing * a 64-byte GET_DESCRIPTOR request. This is what Windows does, * so it may help with some non-standards-compliant devices. * Otherwise we start with SET_ADDRESS and then try to read the * first 8 bytes of the device descriptor to get the ep0 maxpacket * value. */ for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { if (USE_NEW_SCHEME(retry_counter)) { struct usb_device_descriptor *buf; int r = 0; #define GET_DESCRIPTOR_BUFSIZE 64 buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); if (!buf) { retval = -ENOMEM; continue; } /* Use a short timeout the first time through, * so that recalcitrant full-speed devices with * 8- or 16-byte ep0-maxpackets won't slow things * down tremendously by NAKing the unexpectedly * early status stage. Also, retry on all errors; * some devices are flakey. */ for (j = 0; j < 3; ++j) { buf->bMaxPacketSize0 = 0; r = usb_control_msg(udev, usb_rcvaddr0pipe(), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, (i ? USB_CTRL_GET_TIMEOUT : 1000)); switch (buf->bMaxPacketSize0) { case 8: case 16: case 32: case 64: if (buf->bDescriptorType == USB_DT_DEVICE) { r = 0; break; } /* FALL THROUGH */ default: if (r == 0) r = -EPROTO; break; } if (r == 0) break; } udev->descriptor.bMaxPacketSize0 = buf->bMaxPacketSize0; kfree(buf); retval = hub_port_reset(hub, port1, udev, delay); if (retval < 0) /* error or disconnect */ goto fail; if (oldspeed != udev->speed) { dev_dbg(&udev->dev, "device reset changed speed!\n"); retval = -ENODEV; goto fail; } if (r) { dev_err(&udev->dev, "device descriptor " "read/%s, error %d\n", "64", r); retval = -EMSGSIZE; continue; } #undef GET_DESCRIPTOR_BUFSIZE } for (j = 0; j < SET_ADDRESS_TRIES; ++j) { retval = hub_set_address(udev); if (retval >= 0) break; msleep(200); } if (retval < 0) { dev_err(&udev->dev, "device not accepting address %d, error %d\n", udev->devnum, retval); goto fail; } /* cope with hardware quirkiness: * - let SET_ADDRESS settle, some device hardware wants it * - read ep0 maxpacket even for high and low speed, */ msleep(10); if (USE_NEW_SCHEME(retry_counter)) break; retval = usb_get_device_descriptor(udev, 8); if (retval < 8) { dev_err(&udev->dev, "device descriptor " "read/%s, error %d\n", "8", retval); if (retval >= 0) retval = -EMSGSIZE; } else { retval = 0; break; } } if (retval) goto fail; i = udev->descriptor.bMaxPacketSize0; if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { if (udev->speed != USB_SPEED_FULL || !(i == 8 || i == 16 || i == 32 || i == 64)) { dev_err(&udev->dev, "ep0 maxpacket = %d\n", i); retval = -EMSGSIZE; goto fail; } dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i); ep0_reinit(udev); } retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); if (retval < (signed)sizeof(udev->descriptor)) { dev_err(&udev->dev, "device descriptor read/%s, error %d\n", "all", retval); if (retval >= 0) retval = -ENOMSG; goto fail; } retval = 0; fail: if (retval) hub_port_disable(hub, port1, 0); mutex_unlock(&usb_address0_mutex); return retval; } static void check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) { struct usb_qualifier_descriptor *qual; int status; qual = kmalloc (sizeof *qual, SLAB_KERNEL); if (qual == NULL) return; status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0, qual, sizeof *qual); if (status == sizeof *qual) { dev_info(&udev->dev, "not running at top speed; " "connect to a high speed hub\n"); /* hub LEDs are probably harder to miss than syslog */ if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_GREEN_BLINK; schedule_work (&hub->leds); } } kfree(qual); } static unsigned hub_power_remaining (struct usb_hub *hub) { struct usb_device *hdev = hub->hdev; int remaining; int port1; if (!hub->limited_power) return 0; remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent; for (port1 = 1; port1 <= hdev->maxchild; ++port1) { struct usb_device *udev = hdev->children[port1 - 1]; int delta; if (!udev) continue; /* Unconfigured devices may not use more than 100mA, * or 8mA for OTG ports */ if (udev->actconfig) delta = udev->actconfig->desc.bMaxPower * 2; else if (port1 != udev->bus->otg_port || hdev->parent) delta = 100; else delta = 8; if (delta > hub->mA_per_port) dev_warn(&udev->dev, "%dmA is over %umA budget " "for port %d!\n", delta, hub->mA_per_port, port1); remaining -= delta; } if (remaining < 0) { dev_warn(hub->intfdev, "%dmA over power budget!\n", - remaining); remaining = 0; } return remaining; } /* Handle physical or logical connection change events. * This routine is called when: * a port connection-change occurs; * a port enable-change occurs (often caused by EMI); * usb_reset_device() encounters changed descriptors (as from * a firmware download) * caller already locked the hub */ static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange) { struct usb_device *hdev = hub->hdev; struct device *hub_dev = hub->intfdev; u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); int status, i; dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port1, portstatus, portchange, portspeed (portstatus)); if (hub->has_indicators) { set_port_led(hub, port1, HUB_LED_AUTO); hub->indicator[port1-1] = INDICATOR_AUTO; } /* Disconnect any existing devices under this port */ if (hdev->children[port1-1]) usb_disconnect(&hdev->children[port1-1]); clear_bit(port1, hub->change_bits); #ifdef CONFIG_USB_OTG /* during HNP, don't repeat the debounce */ if (hdev->bus->is_b_host) portchange &= ~USB_PORT_STAT_C_CONNECTION; #endif if (portchange & USB_PORT_STAT_C_CONNECTION) { status = hub_port_debounce(hub, port1); if (status < 0) { dev_err (hub_dev, "connect-debounce failed, port %d disabled\n", port1); goto done; } portstatus = status; } /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) { /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 && !(portstatus & (1 << USB_PORT_FEAT_POWER))) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); if (portstatus & USB_PORT_STAT_ENABLE) goto done; return; } #ifdef CONFIG_USB_SUSPEND /* If something is connected, but the port is suspended, wake it up. */ if (portstatus & USB_PORT_STAT_SUSPEND) { status = hub_port_resume(hub, port1, NULL); if (status < 0) { dev_dbg(hub_dev, "can't clear suspend on port %d; %d\n", port1, status); goto done; } } #endif for (i = 0; i < SET_CONFIG_TRIES; i++) { struct usb_device *udev; /* reallocate for each attempt, since references * to the previous one can escape in various ways */ udev = usb_alloc_dev(hdev, hdev->bus, port1); if (!udev) { dev_err (hub_dev, "couldn't allocate port %d usb_device\n", port1); goto done; } usb_set_device_state(udev, USB_STATE_POWERED); udev->speed = USB_SPEED_UNKNOWN; udev->bus_mA = hub->mA_per_port; /* set the address */ choose_address(udev); if (udev->devnum <= 0) { status = -ENOTCONN; /* Don't retry */ goto loop; } /* reset and get descriptor */ status = hub_port_init(hub, udev, port1, i); if (status < 0) goto loop; /* consecutive bus-powered hubs aren't reliable; they can * violate the voltage drop budget. if the new child has * a "powered" LED, users should notice we didn't enable it * (without reading syslog), even without per-port LEDs * on the parent. */ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && udev->bus_mA <= 100) { u16 devstat; status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstat); if (status < 2) { dev_dbg(&udev->dev, "get status %d ?\n", status); goto loop_disable; } le16_to_cpus(&devstat); if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_err(&udev->dev, "can't connect bus-powered hub " "to this port\n"); if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_AMBER_BLINK; schedule_work (&hub->leds); } status = -ENOTCONN; /* Don't retry */ goto loop_disable; } } /* check for devices running slower than they could */ if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) check_highspeed (hub, udev, port1); /* Store the parent's children[] pointer. At this point * udev becomes globally accessible, although presumably * no one will look at it until hdev is unlocked. */ status = 0; /* We mustn't add new devices if the parent hub has * been disconnected; we would race with the * recursively_mark_NOTATTACHED() routine. */ spin_lock_irq(&device_state_lock); if (hdev->state == USB_STATE_NOTATTACHED) status = -ENOTCONN; else hdev->children[port1-1] = udev; spin_unlock_irq(&device_state_lock); /* Run it through the hoops (find a driver, etc) */ if (!status) { status = usb_new_device(udev); if (status) { spin_lock_irq(&device_state_lock); hdev->children[port1-1] = NULL; spin_unlock_irq(&device_state_lock); } } if (status) goto loop_disable; status = hub_power_remaining(hub); if (status) dev_dbg(hub_dev, "%dmA power budget left\n", status); return; loop_disable: hub_port_disable(hub, port1, 1); loop: ep0_reinit(udev); release_address(udev); usb_put_dev(udev); if (status == -ENOTCONN) break; } done: hub_port_disable(hub, port1, 1); } static void hub_events(void) { struct list_head *tmp; struct usb_device *hdev; struct usb_interface *intf; struct usb_hub *hub; struct device *hub_dev; u16 hubstatus; u16 hubchange; u16 portstatus; u16 portchange; int i, ret; int connect_change; /* * We restart the list every time to avoid a deadlock with * deleting hubs downstream from this one. This should be * safe since we delete the hub from the event list. * Not the most efficient, but avoids deadlocks. */ while (1) { /* Grab the first entry at the beginning of the list */