1 |
dmorgan |
1357 |
#define _GNU_SOURCE |
2 |
|
|
#include <stdio.h> |
3 |
|
|
#include <stdlib.h> |
4 |
|
|
#include <string.h> |
5 |
|
|
#include <unistd.h> |
6 |
|
|
#include "libldetect.h" |
7 |
|
|
#include "common.h" |
8 |
|
|
#include "names.h" |
9 |
|
|
|
10 |
|
|
static char *proc_usb_path_default = "/proc/bus/usb/devices"; |
11 |
|
|
char *proc_usb_path = NULL; |
12 |
|
|
|
13 |
|
|
static void build_text(struct pciusb_entry *e, char *vendor_text, char *product_text) { |
14 |
|
|
if(e) { |
15 |
|
|
if(!vendor_text) { |
16 |
|
|
char const* vendorname; |
17 |
|
|
vendorname = names_vendor(e->vendor); |
18 |
|
|
if(vendorname) { |
19 |
|
|
vendor_text = malloc(strlen(vendorname)+2); |
20 |
|
|
sprintf(vendor_text, "%s|", vendorname); |
21 |
|
|
} else |
22 |
|
|
vendor_text = strdup("Unknown|"); |
23 |
|
|
} |
24 |
|
|
if(!product_text) { |
25 |
|
|
char const* productname; |
26 |
|
|
productname = names_product(e->vendor, e->device); |
27 |
|
|
if(productname) { |
28 |
|
|
product_text = strdup(productname); |
29 |
|
|
} else |
30 |
|
|
product_text = strdup("Unknown"); |
31 |
|
|
} |
32 |
|
|
vendor_text = realloc(vendor_text, strlen(vendor_text)+strlen(product_text)+1); |
33 |
|
|
e->text = vendor_text; |
34 |
|
|
strcat(e->text, product_text); |
35 |
|
|
free(product_text); |
36 |
|
|
} |
37 |
|
|
} |
38 |
|
|
|
39 |
|
|
|
40 |
|
|
extern struct pciusb_entries usb_probe(void) { |
41 |
|
|
FILE *f; |
42 |
|
|
char buf[BUF_SIZE]; |
43 |
|
|
int line; |
44 |
|
|
struct pciusb_entries r; |
45 |
|
|
struct pciusb_entry *e = NULL; |
46 |
|
|
char *vendor_text = NULL, *product_text = NULL; |
47 |
|
|
r.nb = 0; |
48 |
|
|
|
49 |
|
|
names_init("/usr/share/usb.ids"); |
50 |
|
|
if (!(f = fopen(proc_usb_path ? proc_usb_path : proc_usb_path_default, "r"))) { |
51 |
|
|
if (proc_usb_path) { |
52 |
|
|
char *err_msg; |
53 |
|
|
asprintf(&err_msg, "unable to open \"%s\"\n" |
54 |
|
|
"You may have passed a wrong argument to the \"-u\" option.\n" |
55 |
|
|
"fopen() sets errno to", proc_usb_path); |
56 |
|
|
perror(err_msg); |
57 |
|
|
free(err_msg); |
58 |
|
|
} |
59 |
|
|
r.entries = NULL; |
60 |
|
|
return r; |
61 |
|
|
} |
62 |
|
|
|
63 |
|
|
r.entries = malloc(sizeof(struct pciusb_entry) * MAX_DEVICES); |
64 |
|
|
/* for further information on the format parsed by this state machine, |
65 |
|
|
* read /usr/share/doc/kernel-doc-X.Y.Z/usb/proc_usb_info.txt */ |
66 |
|
|
for(line = 1; fgets(buf, sizeof(buf) - 1, f) && r.nb < MAX_DEVICES; line++) { |
67 |
|
|
|
68 |
|
|
switch (buf[0]) { |
69 |
|
|
case 'T': { |
70 |
|
|
unsigned short pci_bus, pci_device, usb_port; |
71 |
|
|
build_text(e, vendor_text, product_text); |
72 |
|
|
vendor_text = NULL; |
73 |
|
|
product_text = NULL; |
74 |
|
|
e = &r.entries[r.nb++]; |
75 |
|
|
pciusb_initialize(e); |
76 |
|
|
|
77 |
|
|
if (sscanf(buf, "T: Bus=%02hd Lev=%*02d Prnt=%*04d Port=%02hd Cnt=%*02d Dev#=%3hd Spd=%*3s MxCh=%*2d", &pci_bus, &usb_port, &pci_device) == 3) { |
78 |
|
|
e->pci_bus = pci_bus; |
79 |
|
|
e->pci_device = pci_device; |
80 |
|
|
e->usb_port = usb_port; |
81 |
|
|
} else fprintf(stderr, "%s %d: unknown ``T'' line\n", proc_usb_path, line); |
82 |
|
|
break; |
83 |
|
|
} |
84 |
|
|
case 'P': { |
85 |
|
|
unsigned short vendor, device; |
86 |
|
|
if (sscanf(buf, "P: Vendor=%hx ProdID=%hx", &vendor, &device) == 2) { |
87 |
|
|
e->vendor = vendor; |
88 |
|
|
e->device = device; |
89 |
|
|
} else fprintf(stderr, "%s %d: unknown ``P'' line\n", proc_usb_path, line); |
90 |
|
|
break; |
91 |
|
|
} |
92 |
|
|
case 'I': if (e->class_id == 0 || e->module == NULL) { |
93 |
|
|
char driver[50]; |
94 |
|
|
int class_id, sub, prot = 0; |
95 |
|
|
if (sscanf(buf, "I:* If#=%*2d Alt=%*2d #EPs=%*2d Cls=%02x(%*5c) Sub=%02x Prot=%02x Driver=%s", &class_id, &sub, &prot, driver) >= 3) { |
96 |
|
|
unsigned long cid = (class_id * 0x100 + sub) * 0x100 + prot; |
97 |
|
|
if (e->class_id == 0) |
98 |
|
|
e->class_id = cid; |
99 |
|
|
if (strncmp(driver, "(none)", 6)) { |
100 |
|
|
char *p; |
101 |
|
|
/* Get current class if we are on the first one having used by a driver */ |
102 |
|
|
e->class_id = cid; |
103 |
|
|
e->module = strdup(driver); |
104 |
|
|
/* replace '-' characters with '_' to be compliant with modnames from modaliases */ |
105 |
|
|
p = e->module; |
106 |
|
|
while (p && *p) { |
107 |
|
|
if (*p == '-') *p = '_'; |
108 |
|
|
p++; |
109 |
|
|
} |
110 |
|
|
} |
111 |
|
|
/* see linux/sound/usb/usbaudio.c::usb_audio_ids */ |
112 |
|
|
if (e->class_id == (0x1*0x100+ 0x01)) /* USB_AUDIO_CLASS*0x100 + USB_SUBCLASS_AUDIO_CONTROL*/ |
113 |
|
|
e->module = strdup("snd_usb_audio"); |
114 |
|
|
|
115 |
|
|
} else if (sscanf(buf, "I: If#=%*2d Alt=%*2d #EPs=%*2d Cls=%02x(%*5c) Sub=%02x Prot=%02x Driver=%s", &class_id, &sub, &prot, driver) >= 3) { |
116 |
|
|
/* Ignore interfaces not active */ |
117 |
|
|
} else fprintf(stderr, "%s %d: unknown ``I'' line\n", proc_usb_path, line); |
118 |
|
|
break; |
119 |
|
|
} |
120 |
|
|
case 'S': { |
121 |
|
|
int offset; |
122 |
|
|
char dummy; |
123 |
|
|
size_t length = strlen(buf) -1; |
124 |
|
|
if (sscanf(buf, "S: Manufacturer=%n%c", &offset, &dummy) == 1) { |
125 |
|
|
buf[length] = '|'; /* replacing '\n' by '|' */ |
126 |
|
|
vendor_text = strdup(buf + offset); |
127 |
|
|
} else if (sscanf(buf, "S: Product=%n%c", &offset, &dummy) == 1) { |
128 |
|
|
buf[length] = 0; /* removing '\n' */ |
129 |
|
|
product_text = strdup(buf + offset); |
130 |
|
|
} |
131 |
|
|
} |
132 |
|
|
} |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
build_text(e, vendor_text, product_text); |
136 |
|
|
|
137 |
|
|
fclose(f); |
138 |
|
|
|
139 |
|
|
/* shrink to real size */ |
140 |
|
|
r.entries = realloc(r.entries, sizeof(struct pciusb_entry) * r.nb); |
141 |
|
|
|
142 |
|
|
pciusb_find_modules(&r, "usbtable", DO_NOT_LOAD, 0); |
143 |
|
|
return r; |
144 |
|
|
} |
145 |
|
|
|