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