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
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
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
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
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
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
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