1 /* $FreeBSD: head/tools/tools/bus_autoconf/bus_usb.c 233110 2012-03-18 09:47:27Z hselasky $ */
2
3 /*-
4 * Copyright (c) 2011 Hans Petter Selasky. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <err.h>
33 #include <sysexits.h>
34 #include <unistd.h>
35 #include <sys/queue.h>
36
37 #include "bus_autoconf.h"
38 #include "bus_sections.h"
39 #include "bus_usb.h"
40
41 struct usb_blob;
42 typedef TAILQ_HEAD(,usb_blob) usb_blob_head_t;
43 typedef TAILQ_ENTRY(usb_blob) usb_blob_entry_t;
44
45 static usb_blob_head_t usb_blob_head = TAILQ_HEAD_INITIALIZER(usb_blob_head);
46 static uint32_t usb_blob_count;
47
48 struct usb_blob {
49 usb_blob_entry_t entry;
50 struct usb_device_id temp;
51 };
52
53 /*
54 * To ensure that the correct USB driver is loaded, the driver having
55 * the most information about the device must be probed first. Then
56 * more generic drivers shall be probed.
57 */
58 static int
usb_compare(const void * _a,const void * _b)59 usb_compare(const void *_a, const void *_b)
60 {
61 const struct usb_device_id *a = _a;
62 const struct usb_device_id *b = _b;
63 int retval;
64
65 /* vendor matches first */
66
67 if (a->match_flag_vendor > b->match_flag_vendor)
68 return (-1);
69 if (a->match_flag_vendor < b->match_flag_vendor)
70 return (1);
71
72 /* product matches first */
73
74 if (a->match_flag_product > b->match_flag_product)
75 return (-1);
76 if (a->match_flag_product < b->match_flag_product)
77 return (1);
78
79 /* device class matches first */
80
81 if (a->match_flag_dev_class > b->match_flag_dev_class)
82 return (-1);
83 if (a->match_flag_dev_class < b->match_flag_dev_class)
84 return (1);
85
86 if (a->match_flag_dev_subclass > b->match_flag_dev_subclass)
87 return (-1);
88 if (a->match_flag_dev_subclass < b->match_flag_dev_subclass)
89 return (1);
90
91 /* interface class matches first */
92
93 if (a->match_flag_int_class > b->match_flag_int_class)
94 return (-1);
95 if (a->match_flag_int_class < b->match_flag_int_class)
96 return (1);
97
98 if (a->match_flag_int_subclass > b->match_flag_int_subclass)
99 return (-1);
100 if (a->match_flag_int_subclass < b->match_flag_int_subclass)
101 return (1);
102
103 if (a->match_flag_int_protocol > b->match_flag_int_protocol)
104 return (-1);
105 if (a->match_flag_int_protocol < b->match_flag_int_protocol)
106 return (1);
107
108 /* then sort according to value */
109
110 if (a->idVendor > b->idVendor)
111 return (1);
112 if (a->idVendor < b->idVendor)
113 return (-1);
114 if (a->idProduct > b->idProduct)
115 return (1);
116 if (a->idProduct < b->idProduct)
117 return (-1);
118 if (a->bDeviceClass > b->bDeviceClass)
119 return (1);
120 if (a->bDeviceClass < b->bDeviceClass)
121 return (-1);
122 if (a->bDeviceSubClass > b->bDeviceSubClass)
123 return (1);
124 if (a->bDeviceSubClass < b->bDeviceSubClass)
125 return (-1);
126 if (a->bDeviceProtocol > b->bDeviceProtocol)
127 return (1);
128 if (a->bDeviceProtocol < b->bDeviceProtocol)
129 return (-1);
130 if (a->bInterfaceClass > b->bInterfaceClass)
131 return (1);
132 if (a->bInterfaceClass < b->bInterfaceClass)
133 return (-1);
134 if (a->bInterfaceSubClass > b->bInterfaceSubClass)
135 return (1);
136 if (a->bInterfaceSubClass < b->bInterfaceSubClass)
137 return (-1);
138 if (a->bInterfaceProtocol > b->bInterfaceProtocol)
139 return (1);
140 if (a->bInterfaceProtocol < b->bInterfaceProtocol)
141 return (-1);
142
143 /* in the end sort by module name and mode */
144
145 retval = strcmp(a->module_name, b->module_name);
146 if (retval == 0)
147 retval = strcmp(a->module_mode, b->module_mode);
148 return (retval);
149 }
150
151 static void
usb_sort_entries(struct usb_device_id * id,uint32_t nid)152 usb_sort_entries(struct usb_device_id *id, uint32_t nid)
153 {
154 qsort(id, nid, sizeof(*id), &usb_compare);
155 }
156
157 static void
usb_import_entry(struct usb_device_id * id,const char * type,const char * module,const uint8_t * ptr,uint16_t size)158 usb_import_entry(struct usb_device_id *id, const char *type,
159 const char *module, const uint8_t *ptr, uint16_t size)
160 {
161 const char *mode;
162
163 if (strstr(type, "_host_"))
164 mode = "host";
165 else if (strstr(type, "_device_"))
166 mode = "device";
167 else
168 mode = "(host|device)";
169
170 strlcpy(id->module_name, module, sizeof(id->module_name));
171 strlcpy(id->module_mode, mode, sizeof(id->module_mode));
172
173 /* import data from binary object */
174
175 if (format_get_field(type, "mfl_vendor", ptr, size))
176 id->match_flag_vendor = 1;
177 if (format_get_field(type, "mfl_product", ptr, size))
178 id->match_flag_product = 1;
179 if (format_get_field(type, "mfl_dev_lo", ptr, size))
180 id->match_flag_dev_lo = 1;
181 if (format_get_field(type, "mfl_dev_hi", ptr, size))
182 id->match_flag_dev_hi = 1;
183 if (format_get_field(type, "mfl_dev_class", ptr, size))
184 id->match_flag_dev_class = 1;
185 if (format_get_field(type, "mfl_dev_subclass", ptr, size))
186 id->match_flag_dev_subclass = 1;
187 if (format_get_field(type, "mfl_dev_protocol", ptr, size))
188 id->match_flag_dev_protocol = 1;
189 if (format_get_field(type, "mfl_int_class", ptr, size))
190 id->match_flag_int_class = 1;
191 if (format_get_field(type, "mfl_int_subclass", ptr, size))
192 id->match_flag_int_subclass = 1;
193 if (format_get_field(type, "mfl_int_protocol", ptr, size))
194 id->match_flag_int_protocol = 1;
195
196 id->idVendor = format_get_field(type, "idVendor[0]", ptr, size) |
197 (format_get_field(type, "idVendor[1]", ptr, size) << 8);
198 id->idProduct = format_get_field(type, "idProduct[0]", ptr, size) |
199 (format_get_field(type, "idProduct[1]", ptr, size) << 8);
200
201 id->bcdDevice_lo = format_get_field(type, "bcdDevice_lo[0]", ptr, size) |
202 (format_get_field(type, "bcdDevice_lo[1]", ptr, size) << 8);
203
204 id->bcdDevice_hi = format_get_field(type, "bcdDevice_hi[0]", ptr, size) |
205 (format_get_field(type, "bcdDevice_hi[1]", ptr, size) << 8);
206
207 id->bDeviceClass = format_get_field(type, "bDeviceClass", ptr, size);
208 id->bDeviceSubClass = format_get_field(type, "bDeviceSubClass", ptr, size);
209 id->bDeviceProtocol = format_get_field(type, "bDeviceProtocol", ptr, size);
210
211 id->bInterfaceClass = format_get_field(type, "bInterfaceClass", ptr, size);
212 id->bInterfaceSubClass = format_get_field(type, "bInterfaceSubClass", ptr, size);
213 id->bInterfaceProtocol = format_get_field(type, "bInterfaceProtocol", ptr, size);
214
215 if (format_get_field(type, "mf_vendor", ptr, size))
216 id->match_flag_vendor = 1;
217 if (format_get_field(type, "mf_product", ptr, size))
218 id->match_flag_product = 1;
219 if (format_get_field(type, "mf_dev_lo", ptr, size))
220 id->match_flag_dev_lo = 1;
221 if (format_get_field(type, "mf_dev_hi", ptr, size))
222 id->match_flag_dev_hi = 1;
223 if (format_get_field(type, "mf_dev_class", ptr, size))
224 id->match_flag_dev_class = 1;
225 if (format_get_field(type, "mf_dev_subclass", ptr, size))
226 id->match_flag_dev_subclass = 1;
227 if (format_get_field(type, "mf_dev_protocol", ptr, size))
228 id->match_flag_dev_protocol = 1;
229 if (format_get_field(type, "mf_int_class", ptr, size))
230 id->match_flag_int_class = 1;
231 if (format_get_field(type, "mf_int_subclass", ptr, size))
232 id->match_flag_int_subclass = 1;
233 if (format_get_field(type, "mf_int_protocol", ptr, size))
234 id->match_flag_int_protocol = 1;
235
236 /* compute some internal fields */
237 id->is_iface = id->match_flag_int_class |
238 id->match_flag_int_protocol |
239 id->match_flag_int_subclass;
240
241 id->is_dev = id->match_flag_dev_class |
242 id->match_flag_dev_subclass;
243
244 id->is_vp = id->match_flag_vendor |
245 id->match_flag_product;
246
247 id->is_any = id->is_vp + id->is_dev + id->is_iface;
248 }
249
250 static uint32_t
usb_dump(struct usb_device_id * id,uint32_t nid)251 usb_dump(struct usb_device_id *id, uint32_t nid)
252 {
253 uint32_t n = 1;
254
255 if (id->is_any) {
256 printf("nomatch 32 {\n"
257 " match \"bus\" \"uhub[0-9]+\";\n"
258 " match \"mode\" \"%s\";\n", id->module_mode);
259 } else {
260 printf("# skipped entry on module %s\n",
261 id->module_name);
262 return (n);
263 }
264
265 if (id->match_flag_vendor) {
266 printf(" match \"vendor\" \"0x%04x\";\n",
267 id->idVendor);
268 }
269 if (id->match_flag_product) {
270 uint32_t x;
271
272 if (id->is_any == 1 && id->is_vp == 1) {
273 /* try to join similar entries */
274 while (n < nid) {
275 if (id[n].is_any != 1 || id[n].is_vp != 1)
276 break;
277 if (id[n].idVendor != id[0].idVendor)
278 break;
279 if (strcmp(id[n].module_name, id[0].module_name))
280 break;
281 if (strcmp(id[n].module_mode, id[0].module_mode))
282 break;
283 n++;
284 }
285 }
286 if (n == 1) {
287 printf(" match \"product\" \"0x%04x\";\n",
288 id->idProduct);
289 } else {
290 printf(" match \"product\" \"(");
291
292 for (x = 0; x != n; x++) {
293 printf("0x%04x%s", id[x].idProduct,
294 (x == (n - 1)) ? "" : "|");
295 }
296
297 printf(")\";\n");
298 }
299 }
300 if (id->match_flag_dev_class) {
301 printf(" match \"devclass\" \"0x%02x\";\n",
302 id->bDeviceClass);
303 }
304 if (id->match_flag_dev_subclass) {
305 printf(" match \"devsubclass\" \"0x%02x\";\n",
306 id->bDeviceSubClass);
307 }
308 if (id->match_flag_int_class) {
309 printf(" match \"intclass\" \"0x%02x\";\n",
310 id->bInterfaceClass);
311 }
312 if (id->match_flag_int_subclass) {
313 printf(" match \"intsubclass\" \"0x%02x\";\n",
314 id->bInterfaceSubClass);
315 }
316 if (id->match_flag_int_protocol) {
317 printf(" match \"intprotocol\" \"0x%02x\";\n",
318 id->bInterfaceProtocol);
319 }
320 printf(" action \"kldload -n %s\";\n"
321 "};\n\n", id->module_name);
322
323 return (n);
324 }
325
326 void
usb_import_entries(const char * section,const char * module,const uint8_t * ptr,uint32_t len)327 usb_import_entries(const char *section, const char *module,
328 const uint8_t *ptr, uint32_t len)
329 {
330 struct usb_blob *pub;
331 uint32_t section_size;
332 uint32_t off;
333
334 section_size = format_get_section_size(section);
335 if (section_size == 0) {
336 errx(EX_DATAERR, "Invalid or non-existing "
337 "section format '%s'", section);
338 }
339 if (len % section_size) {
340 errx(EX_DATAERR, "Length %d is not "
341 "divisible by %d. Section format '%s'",
342 len, section_size, section);
343 }
344 for (off = 0; off != len; off += section_size) {
345 pub = malloc(sizeof(*pub));
346 if (pub == NULL)
347 errx(EX_SOFTWARE, "Out of memory");
348
349 memset(pub, 0, sizeof(*pub));
350
351 usb_import_entry(&pub->temp, section,
352 module, ptr + off, section_size);
353
354 TAILQ_INSERT_TAIL(&usb_blob_head, pub, entry);
355
356 usb_blob_count++;
357 if (usb_blob_count == 0)
358 errx(EX_SOFTWARE, "Too many entries");
359 }
360 }
361
362 void
usb_dump_entries(void)363 usb_dump_entries(void)
364 {
365 struct usb_blob *pub;
366 struct usb_device_id *id;
367 uint32_t x;
368
369 id = malloc(usb_blob_count * sizeof(*id));
370 if (id == NULL)
371 errx(EX_SOFTWARE, "Out of memory");
372
373 /* make linear array of all USB blobs */
374 x = 0;
375 TAILQ_FOREACH(pub, &usb_blob_head, entry)
376 id[x++] = pub->temp;
377
378 usb_sort_entries(id, usb_blob_count);
379
380 for (x = 0; x != usb_blob_count;)
381 x += usb_dump(id + x, usb_blob_count - x);
382
383 free(id);
384
385 printf("# %d USB entries processed\n\n", usb_blob_count);
386 }
387