/* $FreeBSD: head/tools/tools/bus_autoconf/bus_usb.c 233110 2012-03-18 09:47:27Z hselasky $ */ /*- * Copyright (c) 2011 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "bus_autoconf.h" #include "bus_sections.h" #include "bus_usb.h" struct usb_blob; typedef TAILQ_HEAD(,usb_blob) usb_blob_head_t; typedef TAILQ_ENTRY(usb_blob) usb_blob_entry_t; static usb_blob_head_t usb_blob_head = TAILQ_HEAD_INITIALIZER(usb_blob_head); static uint32_t usb_blob_count; struct usb_blob { usb_blob_entry_t entry; struct usb_device_id temp; }; /* * To ensure that the correct USB driver is loaded, the driver having * the most information about the device must be probed first. Then * more generic drivers shall be probed. */ static int usb_compare(const void *_a, const void *_b) { const struct usb_device_id *a = _a; const struct usb_device_id *b = _b; int retval; /* vendor matches first */ if (a->match_flag_vendor > b->match_flag_vendor) return (-1); if (a->match_flag_vendor < b->match_flag_vendor) return (1); /* product matches first */ if (a->match_flag_product > b->match_flag_product) return (-1); if (a->match_flag_product < b->match_flag_product) return (1); /* device class matches first */ if (a->match_flag_dev_class > b->match_flag_dev_class) return (-1); if (a->match_flag_dev_class < b->match_flag_dev_class) return (1); if (a->match_flag_dev_subclass > b->match_flag_dev_subclass) return (-1); if (a->match_flag_dev_subclass < b->match_flag_dev_subclass) return (1); /* interface class matches first */ if (a->match_flag_int_class > b->match_flag_int_class) return (-1); if (a->match_flag_int_class < b->match_flag_int_class) return (1); if (a->match_flag_int_subclass > b->match_flag_int_subclass) return (-1); if (a->match_flag_int_subclass < b->match_flag_int_subclass) return (1); if (a->match_flag_int_protocol > b->match_flag_int_protocol) return (-1); if (a->match_flag_int_protocol < b->match_flag_int_protocol) return (1); /* then sort according to value */ if (a->idVendor > b->idVendor) return (1); if (a->idVendor < b->idVendor) return (-1); if (a->idProduct > b->idProduct) return (1); if (a->idProduct < b->idProduct) return (-1); if (a->bDeviceClass > b->bDeviceClass) return (1); if (a->bDeviceClass < b->bDeviceClass) return (-1); if (a->bDeviceSubClass > b->bDeviceSubClass) return (1); if (a->bDeviceSubClass < b->bDeviceSubClass) return (-1); if (a->bDeviceProtocol > b->bDeviceProtocol) return (1); if (a->bDeviceProtocol < b->bDeviceProtocol) return (-1); if (a->bInterfaceClass > b->bInterfaceClass) return (1); if (a->bInterfaceClass < b->bInterfaceClass) return (-1); if (a->bInterfaceSubClass > b->bInterfaceSubClass) return (1); if (a->bInterfaceSubClass < b->bInterfaceSubClass) return (-1); if (a->bInterfaceProtocol > b->bInterfaceProtocol) return (1); if (a->bInterfaceProtocol < b->bInterfaceProtocol) return (-1); /* in the end sort by module name and mode */ retval = strcmp(a->module_name, b->module_name); if (retval == 0) retval = strcmp(a->module_mode, b->module_mode); return (retval); } static void usb_sort_entries(struct usb_device_id *id, uint32_t nid) { qsort(id, nid, sizeof(*id), &usb_compare); } static void usb_import_entry(struct usb_device_id *id, const char *type, const char *module, const uint8_t *ptr, uint16_t size) { const char *mode; if (strstr(type, "_host_")) mode = "host"; else if (strstr(type, "_device_")) mode = "device"; else mode = "(host|device)"; strlcpy(id->module_name, module, sizeof(id->module_name)); strlcpy(id->module_mode, mode, sizeof(id->module_mode)); /* import data from binary object */ if (format_get_field(type, "mfl_vendor", ptr, size)) id->match_flag_vendor = 1; if (format_get_field(type, "mfl_product", ptr, size)) id->match_flag_product = 1; if (format_get_field(type, "mfl_dev_lo", ptr, size)) id->match_flag_dev_lo = 1; if (format_get_field(type, "mfl_dev_hi", ptr, size)) id->match_flag_dev_hi = 1; if (format_get_field(type, "mfl_dev_class", ptr, size)) id->match_flag_dev_class = 1; if (format_get_field(type, "mfl_dev_subclass", ptr, size)) id->match_flag_dev_subclass = 1; if (format_get_field(type, "mfl_dev_protocol", ptr, size)) id->match_flag_dev_protocol = 1; if (format_get_field(type, "mfl_int_class", ptr, size)) id->match_flag_int_class = 1; if (format_get_field(type, "mfl_int_subclass", ptr, size)) id->match_flag_int_subclass = 1; if (format_get_field(type, "mfl_int_protocol", ptr, size)) id->match_flag_int_protocol = 1; id->idVendor = format_get_field(type, "idVendor[0]", ptr, size) | (format_get_field(type, "idVendor[1]", ptr, size) << 8); id->idProduct = format_get_field(type, "idProduct[0]", ptr, size) | (format_get_field(type, "idProduct[1]", ptr, size) << 8); id->bcdDevice_lo = format_get_field(type, "bcdDevice_lo[0]", ptr, size) | (format_get_field(type, "bcdDevice_lo[1]", ptr, size) << 8); id->bcdDevice_hi = format_get_field(type, "bcdDevice_hi[0]", ptr, size) | (format_get_field(type, "bcdDevice_hi[1]", ptr, size) << 8); id->bDeviceClass = format_get_field(type, "bDeviceClass", ptr, size); id->bDeviceSubClass = format_get_field(type, "bDeviceSubClass", ptr, size); id->bDeviceProtocol = format_get_field(type, "bDeviceProtocol", ptr, size); id->bInterfaceClass = format_get_field(type, "bInterfaceClass", ptr, size); id->bInterfaceSubClass = format_get_field(type, "bInterfaceSubClass", ptr, size); id->bInterfaceProtocol = format_get_field(type, "bInterfaceProtocol", ptr, size); if (format_get_field(type, "mf_vendor", ptr, size)) id->match_flag_vendor = 1; if (format_get_field(type, "mf_product", ptr, size)) id->match_flag_product = 1; if (format_get_field(type, "mf_dev_lo", ptr, size)) id->match_flag_dev_lo = 1; if (format_get_field(type, "mf_dev_hi", ptr, size)) id->match_flag_dev_hi = 1; if (format_get_field(type, "mf_dev_class", ptr, size)) id->match_flag_dev_class = 1; if (format_get_field(type, "mf_dev_subclass", ptr, size)) id->match_flag_dev_subclass = 1; if (format_get_field(type, "mf_dev_protocol", ptr, size)) id->match_flag_dev_protocol = 1; if (format_get_field(type, "mf_int_class", ptr, size)) id->match_flag_int_class = 1; if (format_get_field(type, "mf_int_subclass", ptr, size)) id->match_flag_int_subclass = 1; if (format_get_field(type, "mf_int_protocol", ptr, size)) id->match_flag_int_protocol = 1; /* compute some internal fields */ id->is_iface = id->match_flag_int_class | id->match_flag_int_protocol | id->match_flag_int_subclass; id->is_dev = id->match_flag_dev_class | id->match_flag_dev_subclass; id->is_vp = id->match_flag_vendor | id->match_flag_product; id->is_any = id->is_vp + id->is_dev + id->is_iface; } static uint32_t usb_dump(struct usb_device_id *id, uint32_t nid) { uint32_t n = 1; if (id->is_any) { printf("nomatch 32 {\n" " match \"bus\" \"uhub[0-9]+\";\n" " match \"mode\" \"%s\";\n", id->module_mode); } else { printf("# skipped entry on module %s\n", id->module_name); return (n); } if (id->match_flag_vendor) { printf(" match \"vendor\" \"0x%04x\";\n", id->idVendor); } if (id->match_flag_product) { uint32_t x; if (id->is_any == 1 && id->is_vp == 1) { /* try to join similar entries */ while (n < nid) { if (id[n].is_any != 1 || id[n].is_vp != 1) break; if (id[n].idVendor != id[0].idVendor) break; if (strcmp(id[n].module_name, id[0].module_name)) break; if (strcmp(id[n].module_mode, id[0].module_mode)) break; n++; } } if (n == 1) { printf(" match \"product\" \"0x%04x\";\n", id->idProduct); } else { printf(" match \"product\" \"("); for (x = 0; x != n; x++) { printf("0x%04x%s", id[x].idProduct, (x == (n - 1)) ? "" : "|"); } printf(")\";\n"); } } if (id->match_flag_dev_class) { printf(" match \"devclass\" \"0x%02x\";\n", id->bDeviceClass); } if (id->match_flag_dev_subclass) { printf(" match \"devsubclass\" \"0x%02x\";\n", id->bDeviceSubClass); } if (id->match_flag_int_class) { printf(" match \"intclass\" \"0x%02x\";\n", id->bInterfaceClass); } if (id->match_flag_int_subclass) { printf(" match \"intsubclass\" \"0x%02x\";\n", id->bInterfaceSubClass); } if (id->match_flag_int_protocol) { printf(" match \"intprotocol\" \"0x%02x\";\n", id->bInterfaceProtocol); } printf(" action \"kldload -n %s\";\n" "};\n\n", id->module_name); return (n); } void usb_import_entries(const char *section, const char *module, const uint8_t *ptr, uint32_t len) { struct usb_blob *pub; uint32_t section_size; uint32_t off; section_size = format_get_section_size(section); if (section_size == 0) { errx(EX_DATAERR, "Invalid or non-existing " "section format '%s'", section); } if (len % section_size) { errx(EX_DATAERR, "Length %d is not " "divisible by %d. Section format '%s'", len, section_size, section); } for (off = 0; off != len; off += section_size) { pub = malloc(sizeof(*pub)); if (pub == NULL) errx(EX_SOFTWARE, "Out of memory"); memset(pub, 0, sizeof(*pub)); usb_import_entry(&pub->temp, section, module, ptr + off, section_size); TAILQ_INSERT_TAIL(&usb_blob_head, pub, entry); usb_blob_count++; if (usb_blob_count == 0) errx(EX_SOFTWARE, "Too many entries"); } } void usb_dump_entries(void) { struct usb_blob *pub; struct usb_device_id *id; uint32_t x; id = malloc(usb_blob_count * sizeof(*id)); if (id == NULL) errx(EX_SOFTWARE, "Out of memory"); /* make linear array of all USB blobs */ x = 0; TAILQ_FOREACH(pub, &usb_blob_head, entry) id[x++] = pub->temp; usb_sort_entries(id, usb_blob_count); for (x = 0; x != usb_blob_count;) x += usb_dump(id + x, usb_blob_count - x); free(id); printf("# %d USB entries processed\n\n", usb_blob_count); }