1223534Shselasky/* $FreeBSD$ */
2223534Shselasky
3223534Shselasky/*-
4223534Shselasky * Copyright (c) 2011 Hans Petter Selasky. All rights reserved.
5223534Shselasky *
6223534Shselasky * Redistribution and use in source and binary forms, with or without
7223534Shselasky * modification, are permitted provided that the following conditions
8223534Shselasky * are met:
9223534Shselasky * 1. Redistributions of source code must retain the above copyright
10223534Shselasky *    notice, this list of conditions and the following disclaimer.
11223534Shselasky * 2. Redistributions in binary form must reproduce the above copyright
12223534Shselasky *    notice, this list of conditions and the following disclaimer in the
13223534Shselasky *    documentation and/or other materials provided with the distribution.
14223534Shselasky *
15223534Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16223534Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17223534Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18223534Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19223534Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20223534Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21223534Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22223534Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23223534Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24223534Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25223534Shselasky * SUCH DAMAGE.
26223534Shselasky */
27223534Shselasky
28223534Shselasky#include <stdio.h>
29223534Shselasky#include <stdint.h>
30223534Shselasky#include <stdlib.h>
31223534Shselasky#include <string.h>
32223534Shselasky#include <err.h>
33223534Shselasky#include <sysexits.h>
34223534Shselasky#include <unistd.h>
35223534Shselasky#include <sys/queue.h>
36223534Shselasky
37223534Shselasky#include "bus_autoconf.h"
38223534Shselasky#include "bus_sections.h"
39223534Shselasky#include "bus_usb.h"
40223534Shselasky
41223534Shselaskystruct usb_blob;
42223534Shselaskytypedef TAILQ_HEAD(,usb_blob) usb_blob_head_t;
43223534Shselaskytypedef TAILQ_ENTRY(usb_blob) usb_blob_entry_t;
44223534Shselasky
45223534Shselaskystatic usb_blob_head_t usb_blob_head = TAILQ_HEAD_INITIALIZER(usb_blob_head);
46223534Shselaskystatic uint32_t usb_blob_count;
47223534Shselasky
48223534Shselaskystruct usb_blob {
49223534Shselasky	usb_blob_entry_t entry;
50223534Shselasky	struct usb_device_id temp;
51223534Shselasky};
52223534Shselasky
53223534Shselasky/*
54223534Shselasky * To ensure that the correct USB driver is loaded, the driver having
55223534Shselasky * the most information about the device must be probed first. Then
56223534Shselasky * more generic drivers shall be probed.
57223534Shselasky */
58223534Shselaskystatic int
59223534Shselaskyusb_compare(const void *_a, const void *_b)
60223534Shselasky{
61223534Shselasky	const struct usb_device_id *a = _a;
62223534Shselasky	const struct usb_device_id *b = _b;
63223534Shselasky	int retval;
64223534Shselasky
65223534Shselasky	/* vendor matches first */
66223534Shselasky
67223534Shselasky	if (a->match_flag_vendor > b->match_flag_vendor)
68223534Shselasky		return (-1);
69223534Shselasky	if (a->match_flag_vendor < b->match_flag_vendor)
70223534Shselasky		return (1);
71223534Shselasky
72223534Shselasky	/* product matches first */
73223534Shselasky
74223534Shselasky	if (a->match_flag_product > b->match_flag_product)
75223534Shselasky		return (-1);
76223534Shselasky	if (a->match_flag_product < b->match_flag_product)
77223534Shselasky		return (1);
78223534Shselasky
79223534Shselasky	/* device class matches first */
80223534Shselasky
81223534Shselasky	if (a->match_flag_dev_class > b->match_flag_dev_class)
82223534Shselasky		return (-1);
83223534Shselasky	if (a->match_flag_dev_class < b->match_flag_dev_class)
84223534Shselasky		return (1);
85223534Shselasky
86223534Shselasky	if (a->match_flag_dev_subclass > b->match_flag_dev_subclass)
87223534Shselasky		return (-1);
88223534Shselasky	if (a->match_flag_dev_subclass < b->match_flag_dev_subclass)
89223534Shselasky		return (1);
90223534Shselasky
91223534Shselasky	/* interface class matches first */
92223534Shselasky
93223534Shselasky	if (a->match_flag_int_class > b->match_flag_int_class)
94223534Shselasky		return (-1);
95223534Shselasky	if (a->match_flag_int_class < b->match_flag_int_class)
96223534Shselasky		return (1);
97223534Shselasky
98223534Shselasky	if (a->match_flag_int_subclass > b->match_flag_int_subclass)
99223534Shselasky		return (-1);
100223534Shselasky	if (a->match_flag_int_subclass < b->match_flag_int_subclass)
101223534Shselasky		return (1);
102223534Shselasky
103223534Shselasky	if (a->match_flag_int_protocol > b->match_flag_int_protocol)
104223534Shselasky		return (-1);
105223534Shselasky	if (a->match_flag_int_protocol < b->match_flag_int_protocol)
106223534Shselasky		return (1);
107223534Shselasky
108223534Shselasky	/* then sort according to value */
109223534Shselasky
110223534Shselasky	if (a->idVendor > b->idVendor)
111223534Shselasky		return (1);
112223534Shselasky	if (a->idVendor < b->idVendor)
113223534Shselasky		return (-1);
114223534Shselasky	if (a->idProduct > b->idProduct)
115223534Shselasky		return (1);
116223534Shselasky	if (a->idProduct < b->idProduct)
117223534Shselasky		return (-1);
118223534Shselasky	if (a->bDeviceClass > b->bDeviceClass)
119223534Shselasky		return (1);
120223534Shselasky	if (a->bDeviceClass < b->bDeviceClass)
121223534Shselasky		return (-1);
122223534Shselasky	if (a->bDeviceSubClass > b->bDeviceSubClass)
123223534Shselasky		return (1);
124223534Shselasky	if (a->bDeviceSubClass < b->bDeviceSubClass)
125223534Shselasky		return (-1);
126223534Shselasky	if (a->bDeviceProtocol > b->bDeviceProtocol)
127223534Shselasky		return (1);
128223534Shselasky	if (a->bDeviceProtocol < b->bDeviceProtocol)
129223534Shselasky		return (-1);
130223534Shselasky	if (a->bInterfaceClass > b->bInterfaceClass)
131223534Shselasky		return (1);
132223534Shselasky	if (a->bInterfaceClass < b->bInterfaceClass)
133223534Shselasky		return (-1);
134223534Shselasky	if (a->bInterfaceSubClass > b->bInterfaceSubClass)
135223534Shselasky		return (1);
136223534Shselasky	if (a->bInterfaceSubClass < b->bInterfaceSubClass)
137223534Shselasky		return (-1);
138223534Shselasky	if (a->bInterfaceProtocol > b->bInterfaceProtocol)
139223534Shselasky		return (1);
140223534Shselasky	if (a->bInterfaceProtocol < b->bInterfaceProtocol)
141223534Shselasky		return (-1);
142223534Shselasky
143223534Shselasky	/* in the end sort by module name and mode */
144223534Shselasky
145223534Shselasky	retval = strcmp(a->module_name, b->module_name);
146223534Shselasky	if (retval == 0)
147223534Shselasky		retval = strcmp(a->module_mode, b->module_mode);
148223534Shselasky	return (retval);
149223534Shselasky}
150223534Shselasky
151223534Shselaskystatic void
152223534Shselaskyusb_sort_entries(struct usb_device_id *id, uint32_t nid)
153223534Shselasky{
154223534Shselasky	qsort(id, nid, sizeof(*id), &usb_compare);
155223534Shselasky}
156223534Shselasky
157223534Shselaskystatic void
158223534Shselaskyusb_import_entry(struct usb_device_id *id, const char *type,
159223534Shselasky    const char *module, const uint8_t *ptr, uint16_t size)
160223534Shselasky{
161223534Shselasky	const char *mode;
162223534Shselasky
163223534Shselasky	if (strstr(type, "_host_"))
164223534Shselasky		mode = "host";
165223534Shselasky	else if (strstr(type, "_device_"))
166223534Shselasky		mode = "device";
167223534Shselasky	else
168223534Shselasky		mode = "(host|device)";
169223534Shselasky
170223534Shselasky	strlcpy(id->module_name, module, sizeof(id->module_name));
171223534Shselasky	strlcpy(id->module_mode, mode, sizeof(id->module_mode));
172223534Shselasky
173223534Shselasky	/* import data from binary object */
174223534Shselasky
175223534Shselasky	if (format_get_field(type, "mfl_vendor", ptr, size))
176223534Shselasky		id->match_flag_vendor = 1;
177223534Shselasky	if (format_get_field(type, "mfl_product", ptr, size))
178223534Shselasky		id->match_flag_product = 1;
179223534Shselasky	if (format_get_field(type, "mfl_dev_lo", ptr, size))
180223534Shselasky		id->match_flag_dev_lo = 1;
181223534Shselasky	if (format_get_field(type, "mfl_dev_hi", ptr, size))
182223534Shselasky		id->match_flag_dev_hi = 1;
183223534Shselasky	if (format_get_field(type, "mfl_dev_class", ptr, size))
184223534Shselasky		id->match_flag_dev_class = 1;
185223534Shselasky	if (format_get_field(type, "mfl_dev_subclass", ptr, size))
186223534Shselasky		id->match_flag_dev_subclass = 1;
187223534Shselasky	if (format_get_field(type, "mfl_dev_protocol", ptr, size))
188223534Shselasky		id->match_flag_dev_protocol = 1;
189223534Shselasky	if (format_get_field(type, "mfl_int_class", ptr, size))
190223534Shselasky		id->match_flag_int_class = 1;
191223534Shselasky	if (format_get_field(type, "mfl_int_subclass", ptr, size))
192223534Shselasky		id->match_flag_int_subclass = 1;
193223534Shselasky	if (format_get_field(type, "mfl_int_protocol", ptr, size))
194223534Shselasky		id->match_flag_int_protocol = 1;
195223534Shselasky
196223534Shselasky	id->idVendor = format_get_field(type, "idVendor[0]", ptr, size) |
197223534Shselasky	    (format_get_field(type, "idVendor[1]", ptr, size) << 8);
198223534Shselasky	id->idProduct = format_get_field(type, "idProduct[0]", ptr, size) |
199223534Shselasky	    (format_get_field(type, "idProduct[1]", ptr, size) << 8);
200223534Shselasky
201223534Shselasky	id->bcdDevice_lo = format_get_field(type, "bcdDevice_lo[0]", ptr, size) |
202223534Shselasky	    (format_get_field(type, "bcdDevice_lo[1]", ptr, size) << 8);
203223534Shselasky
204223534Shselasky	id->bcdDevice_hi = format_get_field(type, "bcdDevice_hi[0]", ptr, size) |
205223534Shselasky	    (format_get_field(type, "bcdDevice_hi[1]", ptr, size) << 8);
206223534Shselasky
207223534Shselasky	id->bDeviceClass = format_get_field(type, "bDeviceClass", ptr, size);
208223534Shselasky	id->bDeviceSubClass = format_get_field(type, "bDeviceSubClass", ptr, size);
209223534Shselasky	id->bDeviceProtocol = format_get_field(type, "bDeviceProtocol", ptr, size);
210223534Shselasky
211223534Shselasky	id->bInterfaceClass = format_get_field(type, "bInterfaceClass", ptr, size);
212223534Shselasky	id->bInterfaceSubClass = format_get_field(type, "bInterfaceSubClass", ptr, size);
213223534Shselasky	id->bInterfaceProtocol = format_get_field(type, "bInterfaceProtocol", ptr, size);
214223534Shselasky
215223534Shselasky	if (format_get_field(type, "mf_vendor", ptr, size))
216223534Shselasky		id->match_flag_vendor = 1;
217223534Shselasky	if (format_get_field(type, "mf_product", ptr, size))
218223534Shselasky		id->match_flag_product = 1;
219223534Shselasky	if (format_get_field(type, "mf_dev_lo", ptr, size))
220223534Shselasky		id->match_flag_dev_lo = 1;
221223534Shselasky	if (format_get_field(type, "mf_dev_hi", ptr, size))
222223534Shselasky		id->match_flag_dev_hi = 1;
223223534Shselasky	if (format_get_field(type, "mf_dev_class", ptr, size))
224223534Shselasky		id->match_flag_dev_class = 1;
225223534Shselasky	if (format_get_field(type, "mf_dev_subclass", ptr, size))
226223534Shselasky		id->match_flag_dev_subclass = 1;
227223534Shselasky	if (format_get_field(type, "mf_dev_protocol", ptr, size))
228223534Shselasky		id->match_flag_dev_protocol = 1;
229223534Shselasky	if (format_get_field(type, "mf_int_class", ptr, size))
230223534Shselasky		id->match_flag_int_class = 1;
231223534Shselasky	if (format_get_field(type, "mf_int_subclass", ptr, size))
232223534Shselasky		id->match_flag_int_subclass = 1;
233223534Shselasky	if (format_get_field(type, "mf_int_protocol", ptr, size))
234223534Shselasky		id->match_flag_int_protocol = 1;
235223534Shselasky
236223534Shselasky	/* compute some internal fields */
237223534Shselasky	id->is_iface = id->match_flag_int_class |
238223534Shselasky	    id->match_flag_int_protocol |
239223534Shselasky	    id->match_flag_int_subclass;
240223534Shselasky
241223534Shselasky	id->is_dev = id->match_flag_dev_class |
242223534Shselasky	    id->match_flag_dev_subclass;
243223534Shselasky
244223534Shselasky	id->is_vp = id->match_flag_vendor |
245223534Shselasky	    id->match_flag_product;
246223534Shselasky
247223534Shselasky	id->is_any = id->is_vp + id->is_dev + id->is_iface;
248223534Shselasky}
249223534Shselasky
250223534Shselaskystatic uint32_t
251223534Shselaskyusb_dump(struct usb_device_id *id, uint32_t nid)
252223534Shselasky{
253223534Shselasky	uint32_t n = 1;
254223534Shselasky
255223534Shselasky	if (id->is_any) {
256223534Shselasky		printf("nomatch 32 {\n"
257223534Shselasky		    "	match \"bus\" \"uhub[0-9]+\";\n"
258223534Shselasky		    "	match \"mode\" \"%s\";\n", id->module_mode);
259223534Shselasky	} else {
260223534Shselasky		printf("# skipped entry on module %s\n",
261223534Shselasky		    id->module_name);
262223534Shselasky		return (n);
263223534Shselasky	}
264223534Shselasky
265223534Shselasky	if (id->match_flag_vendor) {
266223534Shselasky		printf("	match \"vendor\" \"0x%04x\";\n",
267223534Shselasky		    id->idVendor);
268223534Shselasky	}
269223534Shselasky	if (id->match_flag_product) {
270223534Shselasky		uint32_t x;
271223534Shselasky
272223534Shselasky		if (id->is_any == 1 && id->is_vp == 1) {
273223534Shselasky			/* try to join similar entries */
274223534Shselasky			while (n < nid) {
275223534Shselasky				if (id[n].is_any != 1 || id[n].is_vp != 1)
276223534Shselasky					break;
277223534Shselasky				if (id[n].idVendor != id[0].idVendor)
278223534Shselasky					break;
279223535Shselasky				if (strcmp(id[n].module_name, id[0].module_name))
280223535Shselasky					break;
281223535Shselasky				if (strcmp(id[n].module_mode, id[0].module_mode))
282223535Shselasky					break;
283223534Shselasky				n++;
284223534Shselasky			}
285223534Shselasky		}
286223534Shselasky		if (n == 1) {
287223534Shselasky			printf("	match \"product\" \"0x%04x\";\n",
288223534Shselasky			    id->idProduct);
289223534Shselasky		} else {
290223534Shselasky			printf("	match \"product\" \"(");
291223534Shselasky
292223534Shselasky			for (x = 0; x != n; x++) {
293223534Shselasky				printf("0x%04x%s", id[x].idProduct,
294223534Shselasky				    (x == (n - 1)) ? "" : "|");
295223534Shselasky			}
296223534Shselasky
297223534Shselasky			printf(")\";\n");
298223534Shselasky		}
299223534Shselasky	}
300223534Shselasky	if (id->match_flag_dev_class) {
301223534Shselasky		printf("	match \"devclass\" \"0x%02x\";\n",
302223534Shselasky		    id->bDeviceClass);
303223534Shselasky	}
304223534Shselasky	if (id->match_flag_dev_subclass) {
305223534Shselasky		printf("	match \"devsubclass\" \"0x%02x\";\n",
306223534Shselasky		    id->bDeviceSubClass);
307223534Shselasky	}
308223534Shselasky	if (id->match_flag_int_class) {
309223534Shselasky		printf("	match \"intclass\" \"0x%02x\";\n",
310223534Shselasky		    id->bInterfaceClass);
311223534Shselasky	}
312223534Shselasky	if (id->match_flag_int_subclass) {
313223534Shselasky		printf("	match \"intsubclass\" \"0x%02x\";\n",
314223534Shselasky		    id->bInterfaceSubClass);
315223534Shselasky	}
316223534Shselasky	if (id->match_flag_int_protocol) {
317223534Shselasky		printf("	match \"intprotocol\" \"0x%02x\";\n",
318223534Shselasky		    id->bInterfaceProtocol);
319223534Shselasky	}
320233110Shselasky	printf("	action \"kldload -n %s\";\n"
321223534Shselasky	    "};\n\n", id->module_name);
322223534Shselasky
323223534Shselasky	return (n);
324223534Shselasky}
325223534Shselasky
326223534Shselaskyvoid
327223534Shselaskyusb_import_entries(const char *section, const char *module,
328223534Shselasky    const uint8_t *ptr, uint32_t len)
329223534Shselasky{
330223534Shselasky	struct usb_blob *pub;
331223534Shselasky	uint32_t section_size;
332223534Shselasky	uint32_t off;
333223534Shselasky
334223534Shselasky	section_size = format_get_section_size(section);
335223534Shselasky	if (section_size == 0) {
336223534Shselasky		errx(EX_DATAERR, "Invalid or non-existing "
337223534Shselasky		    "section format '%s'", section);
338223534Shselasky	}
339223534Shselasky	if (len % section_size) {
340223534Shselasky		errx(EX_DATAERR, "Length %d is not "
341223534Shselasky		    "divisible by %d. Section format '%s'",
342223534Shselasky		    len, section_size, section);
343223534Shselasky	}
344223534Shselasky	for (off = 0; off != len; off += section_size) {
345223534Shselasky		pub = malloc(sizeof(*pub));
346223534Shselasky		if (pub == NULL)
347223534Shselasky			errx(EX_SOFTWARE, "Out of memory");
348223534Shselasky
349223534Shselasky		memset(pub, 0, sizeof(*pub));
350223534Shselasky
351223534Shselasky		usb_import_entry(&pub->temp, section,
352223534Shselasky		    module, ptr + off, section_size);
353223534Shselasky
354223534Shselasky		TAILQ_INSERT_TAIL(&usb_blob_head, pub, entry);
355223534Shselasky
356223534Shselasky		usb_blob_count++;
357223534Shselasky		if (usb_blob_count == 0)
358223534Shselasky			errx(EX_SOFTWARE, "Too many entries");
359223534Shselasky	}
360223534Shselasky}
361223534Shselasky
362223534Shselaskyvoid
363223534Shselaskyusb_dump_entries(void)
364223534Shselasky{
365223534Shselasky	struct usb_blob *pub;
366223534Shselasky	struct usb_device_id *id;
367223534Shselasky	uint32_t x;
368223534Shselasky
369223534Shselasky	id = malloc(usb_blob_count * sizeof(*id));
370223534Shselasky	if (id == NULL)
371223534Shselasky		errx(EX_SOFTWARE, "Out of memory");
372223534Shselasky
373223534Shselasky	/* make linear array of all USB blobs */
374223534Shselasky	x = 0;
375223534Shselasky	TAILQ_FOREACH(pub, &usb_blob_head, entry)
376223534Shselasky	    id[x++] = pub->temp;
377223534Shselasky
378223534Shselasky	usb_sort_entries(id, usb_blob_count);
379223534Shselasky
380223534Shselasky	for (x = 0; x != usb_blob_count;)
381223534Shselasky		x += usb_dump(id + x, usb_blob_count - x);
382223534Shselasky
383223534Shselasky	free(id);
384223534Shselasky
385223534Shselasky	printf("# %d USB entries processed\n\n", usb_blob_count);
386223534Shselasky}
387