1/* $FreeBSD$ */
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
41struct usb_blob;
42typedef TAILQ_HEAD(,usb_blob) usb_blob_head_t;
43typedef TAILQ_ENTRY(usb_blob) usb_blob_entry_t;
44
45static usb_blob_head_t usb_blob_head = TAILQ_HEAD_INITIALIZER(usb_blob_head);
46static uint32_t usb_blob_count;
47
48struct 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 */
58static int
59usb_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
151static void
152usb_sort_entries(struct usb_device_id *id, uint32_t nid)
153{
154	qsort(id, nid, sizeof(*id), &usb_compare);
155}
156
157static void
158usb_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
250static uint32_t
251usb_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
326void
327usb_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
362void
363usb_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