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