1 /* $FreeBSD$ */ 2 /*- 3 * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 4 * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 5 * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/stdint.h> 30 #include <sys/stddef.h> 31 #include <sys/param.h> 32 #include <sys/queue.h> 33 #include <sys/types.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/bus.h> 37 #include <sys/linker_set.h> 38 #include <sys/module.h> 39 #include <sys/lock.h> 40 #include <sys/mutex.h> 41 #include <sys/condvar.h> 42 #include <sys/sysctl.h> 43 #include <sys/sx.h> 44 #include <sys/unistd.h> 45 #include <sys/callout.h> 46 #include <sys/malloc.h> 47 #include <sys/priv.h> 48 49 #include <dev/usb/usb.h> 50 #include <dev/usb/usb_ioctl.h> 51 #include <dev/usb/usbdi.h> 52 #include "usbdevs.h" 53 54 #define USB_DEBUG_VAR usb_debug 55 #include <dev/usb/usb_debug.h> 56 #include <dev/usb/usb_dynamic.h> 57 58 #include <dev/usb/quirk/usb_quirk.h> 59 60 MODULE_DEPEND(usb_quirk, usb, 1, 1, 1); 61 MODULE_VERSION(usb_quirk, 1); 62 63 /* 64 * The following macro adds one or more quirks for a USB device: 65 */ 66 #define USB_QUIRK_ENTRY(v,p,l,h,...) \ 67 .vid = (v), .pid = (p), .lo_rev = (l), .hi_rev = (h), .quirks = { __VA_ARGS__ } 68 69 #define USB_DEV_QUIRKS_MAX 128 70 #define USB_SUB_QUIRKS_MAX 8 71 72 struct usb_quirk_entry { 73 uint16_t vid; 74 uint16_t pid; 75 uint16_t lo_rev; 76 uint16_t hi_rev; 77 uint16_t quirks[USB_SUB_QUIRKS_MAX]; 78 }; 79 80 static struct mtx usb_quirk_mtx; 81 82 static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { 83 {USB_QUIRK_ENTRY(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_LCM, 84 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 85 {USB_QUIRK_ENTRY(USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, 86 0x094, 0x094, UQ_SWAP_UNICODE, UQ_NONE)}, 87 {USB_QUIRK_ENTRY(USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 88 0x0a2, 0x0a2, UQ_BAD_ADC, UQ_NONE)}, 89 {USB_QUIRK_ENTRY(USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 90 0x0a2, 0x0a2, UQ_AU_NO_XU, UQ_NONE)}, 91 {USB_QUIRK_ENTRY(USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 92 0x103, 0x103, UQ_BAD_ADC, UQ_NONE)}, 93 {USB_QUIRK_ENTRY(USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 94 0x000, 0x000, UQ_BAD_AUDIO, UQ_NONE)}, 95 {USB_QUIRK_ENTRY(USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 96 0x110, 0x110, UQ_SPUR_BUT_UP, UQ_NONE)}, 97 {USB_QUIRK_ENTRY(USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 98 0x001, 0x001, UQ_SPUR_BUT_UP, UQ_NONE)}, 99 {USB_QUIRK_ENTRY(USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 100 0x102, 0x102, UQ_BUS_POWERED, UQ_NONE)}, 101 {USB_QUIRK_ENTRY(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 102 0x102, 0x102, UQ_BUS_POWERED, UQ_NONE)}, 103 {USB_QUIRK_ENTRY(USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 104 0x110, 0x110, UQ_POWER_CLAIM, UQ_NONE)}, 105 {USB_QUIRK_ENTRY(USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 106 0x009, 0x009, UQ_AU_NO_FRAC, UQ_NONE)}, 107 {USB_QUIRK_ENTRY(USB_VENDOR_SILICONPORTALS, 108 USB_PRODUCT_SILICONPORTALS_YAPPHONE, 109 0x100, 0x100, UQ_AU_INP_ASYNC, UQ_NONE)}, 110 {USB_QUIRK_ENTRY(USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN53B, 111 0x0000, 0xFFFF, UQ_NO_STRINGS, UQ_NONE)}, 112 {USB_QUIRK_ENTRY(USB_VENDOR_ELSA, USB_PRODUCT_ELSA_MODEM1, 113 0x0000, 0xFFFF, UQ_CFG_INDEX_1, UQ_NONE)}, 114 115 /* 116 * XXX The following quirks should have a more specific revision 117 * number: 118 */ 119 {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_895C, 120 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 121 {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_880C, 122 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 123 {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_815C, 124 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 125 {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_810C, 126 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 127 {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_830C, 128 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 129 {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_1220C, 130 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 131 {USB_QUIRK_ENTRY(USB_VENDOR_XEROX, USB_PRODUCT_XEROX_WCM15, 132 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, 133 /* Devices which should be ignored by uhid */ 134 {USB_QUIRK_ENTRY(USB_VENDOR_APC, USB_PRODUCT_APC_UPS, 135 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 136 {USB_QUIRK_ENTRY(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C550AVR, 137 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 138 {USB_QUIRK_ENTRY(USB_VENDOR_CYBERPOWER, 139 USB_PRODUCT_CYBERPOWER_1500CAVRLCD, 140 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 141 {USB_QUIRK_ENTRY(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, 142 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 143 {USB_QUIRK_ENTRY(USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD2X20, 144 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 145 {USB_QUIRK_ENTRY(USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD4X20, 146 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 147 {USB_QUIRK_ENTRY(USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, 148 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 149 {USB_QUIRK_ENTRY(USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, 150 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 151 {USB_QUIRK_ENTRY(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 152 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 153 {USB_QUIRK_ENTRY(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 154 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, 155 /* Devices which should be ignored by both ukbd and uhid */ 156 {USB_QUIRK_ENTRY(USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_WISPY1A, 157 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)}, 158 {USB_QUIRK_ENTRY(USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY1B, 159 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)}, 160 {USB_QUIRK_ENTRY(USB_VENDOR_TENX, USB_PRODUCT_TENX_UAUDIO0, 161 0x0101, 0x0101, UQ_AUDIO_SWAP_LR, UQ_NONE)}, 162 /* MS keyboards do weird things */ 163 {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, 164 USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE, 165 0x0000, 0xFFFF, UQ_MS_LEADING_BYTE, UQ_NONE)}, 166 {USB_QUIRK_ENTRY(USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY24X, 167 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)}, 168 /* umodem(4) device quirks */ 169 {USB_QUIRK_ENTRY(USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS, 170 0x100, 0x100, UQ_ASSUME_CM_OVER_DATA)}, 171 {USB_QUIRK_ENTRY(USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900, 172 0x000, 0x000, UQ_ASSUME_CM_OVER_DATA)}, 173 {USB_QUIRK_ENTRY(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_T720C, 174 0x001, 0x001, UQ_ASSUME_CM_OVER_DATA)}, 175 {USB_QUIRK_ENTRY(USB_VENDOR_EICON, USB_PRODUCT_EICON_DIVA852, 176 0x100, 0x100, UQ_ASSUME_CM_OVER_DATA)}, 177 {USB_QUIRK_ENTRY(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_ES75, 178 0x000, 0x000, UQ_ASSUME_CM_OVER_DATA)}, 179 {USB_QUIRK_ENTRY(USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_CDMA_MSM, 180 0x0000, 0xFFFF, UQ_ASSUME_CM_OVER_DATA)}, 181 {USB_QUIRK_ENTRY(USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_CDMA_MSM, 182 0x0000, 0xFFFF, UQ_ASSUME_CM_OVER_DATA)}, 183 {USB_QUIRK_ENTRY(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_UM175, 184 0x0000, 0xFFFF, UQ_ASSUME_CM_OVER_DATA)}, 185 186 }; 187 188 static const char *usb_quirk_str[USB_QUIRK_MAX] = { 189 [UQ_NONE] = "UQ_NONE", 190 [UQ_AUDIO_SWAP_LR] = "UQ_AUDIO_SWAP_LR", 191 [UQ_AU_INP_ASYNC] = "UQ_AU_INP_ASYNC", 192 [UQ_AU_NO_FRAC] = "UQ_AU_NO_FRAC", 193 [UQ_AU_NO_XU] = "UQ_AU_NO_XU", 194 [UQ_BAD_ADC] = "UQ_BAD_ADC", 195 [UQ_BAD_AUDIO] = "UQ_BAD_AUDIO", 196 [UQ_BROKEN_BIDIR] = "UQ_BROKEN_BIDIR", 197 [UQ_BUS_POWERED] = "UQ_BUS_POWERED", 198 [UQ_HID_IGNORE] = "UQ_HID_IGNORE", 199 [UQ_KBD_IGNORE] = "UQ_KBD_IGNORE", 200 [UQ_MS_BAD_CLASS] = "UQ_MS_BAD_CLASS", 201 [UQ_MS_LEADING_BYTE] = "UQ_MS_LEADING_BYTE", 202 [UQ_MS_REVZ] = "UQ_MS_REVZ", 203 [UQ_NO_STRINGS] = "UQ_NO_STRINGS", 204 [UQ_OPEN_CLEARSTALL] = "UQ_OPEN_CLEARSTALL", 205 [UQ_POWER_CLAIM] = "UQ_POWER_CLAIM", 206 [UQ_SPUR_BUT_UP] = "UQ_SPUR_BUT_UP", 207 [UQ_SWAP_UNICODE] = "UQ_SWAP_UNICODE", 208 [UQ_CFG_INDEX_1] = "UQ_CFG_INDEX_1", 209 [UQ_CFG_INDEX_2] = "UQ_CFG_INDEX_2", 210 [UQ_CFG_INDEX_3] = "UQ_CFG_INDEX_3", 211 [UQ_CFG_INDEX_4] = "UQ_CFG_INDEX_4", 212 [UQ_CFG_INDEX_0] = "UQ_CFG_INDEX_0", 213 [UQ_ASSUME_CM_OVER_DATA]= "UQ_ASSUME_CM_OVER_DATA", 214 }; 215 216 /*------------------------------------------------------------------------* 217 * usb_quirkstr 218 * 219 * This function converts an USB quirk code into a string. 220 *------------------------------------------------------------------------*/ 221 static const char * 222 usb_quirkstr(uint16_t quirk) 223 { 224 return ((quirk < USB_QUIRK_MAX) ? 225 usb_quirk_str[quirk] : "USB_QUIRK_UNKNOWN"); 226 } 227 228 /*------------------------------------------------------------------------* 229 * usb_test_quirk_by_info 230 * 231 * Returns: 232 * 0: Quirk not found 233 * Else: Quirk found 234 *------------------------------------------------------------------------*/ 235 static uint8_t 236 usb_test_quirk_by_info(const struct usbd_lookup_info *info, uint16_t quirk) 237 { 238 uint16_t x; 239 uint16_t y; 240 241 if (quirk == UQ_NONE) { 242 return (0); 243 } 244 mtx_lock(&usb_quirk_mtx); 245 246 for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { 247 /* see if quirk information does not match */ 248 if ((usb_quirks[x].vid != info->idVendor) || 249 (usb_quirks[x].pid != info->idProduct) || 250 (usb_quirks[x].lo_rev > info->bcdDevice) || 251 (usb_quirks[x].hi_rev < info->bcdDevice)) { 252 continue; 253 } 254 /* lookup quirk */ 255 for (y = 0; y != USB_SUB_QUIRKS_MAX; y++) { 256 if (usb_quirks[x].quirks[y] == quirk) { 257 mtx_unlock(&usb_quirk_mtx); 258 DPRINTF("Found quirk '%s'.\n", usb_quirkstr(quirk)); 259 return (1); 260 } 261 } 262 /* no quirk found */ 263 break; 264 } 265 mtx_unlock(&usb_quirk_mtx); 266 return (0); 267 } 268 269 static struct usb_quirk_entry * 270 usb_quirk_get_entry(uint16_t vid, uint16_t pid, 271 uint16_t lo_rev, uint16_t hi_rev, uint8_t do_alloc) 272 { 273 uint16_t x; 274 275 mtx_assert(&usb_quirk_mtx, MA_OWNED); 276 277 if ((vid | pid | lo_rev | hi_rev) == 0) { 278 /* all zero - special case */ 279 return (usb_quirks + USB_DEV_QUIRKS_MAX - 1); 280 } 281 /* search for an existing entry */ 282 for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { 283 /* see if quirk information does not match */ 284 if ((usb_quirks[x].vid != vid) || 285 (usb_quirks[x].pid != pid) || 286 (usb_quirks[x].lo_rev != lo_rev) || 287 (usb_quirks[x].hi_rev != hi_rev)) { 288 continue; 289 } 290 return (usb_quirks + x); 291 } 292 293 if (do_alloc == 0) { 294 /* no match */ 295 return (NULL); 296 } 297 /* search for a free entry */ 298 for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { 299 /* see if quirk information does not match */ 300 if ((usb_quirks[x].vid | 301 usb_quirks[x].pid | 302 usb_quirks[x].lo_rev | 303 usb_quirks[x].hi_rev) != 0) { 304 continue; 305 } 306 usb_quirks[x].vid = vid; 307 usb_quirks[x].pid = pid; 308 usb_quirks[x].lo_rev = lo_rev; 309 usb_quirks[x].hi_rev = hi_rev; 310 311 return (usb_quirks + x); 312 } 313 314 /* no entry found */ 315 return (NULL); 316 } 317 318 /*------------------------------------------------------------------------* 319 * usb_quirk_ioctl - handle quirk IOCTLs 320 * 321 * Returns: 322 * 0: Success 323 * Else: Failure 324 *------------------------------------------------------------------------*/ 325 static int 326 usb_quirk_ioctl(unsigned long cmd, caddr_t data, 327 int fflag, struct thread *td) 328 { 329 struct usb_gen_quirk *pgq; 330 struct usb_quirk_entry *pqe; 331 uint32_t x; 332 uint32_t y; 333 int err; 334 335 switch (cmd) { 336 case USB_DEV_QUIRK_GET: 337 pgq = (void *)data; 338 x = pgq->index % USB_SUB_QUIRKS_MAX; 339 y = pgq->index / USB_SUB_QUIRKS_MAX; 340 if (y >= USB_DEV_QUIRKS_MAX) { 341 return (EINVAL); 342 } 343 mtx_lock(&usb_quirk_mtx); 344 /* copy out data */ 345 pgq->vid = usb_quirks[y].vid; 346 pgq->pid = usb_quirks[y].pid; 347 pgq->bcdDeviceLow = usb_quirks[y].lo_rev; 348 pgq->bcdDeviceHigh = usb_quirks[y].hi_rev; 349 strlcpy(pgq->quirkname, 350 usb_quirkstr(usb_quirks[y].quirks[x]), 351 sizeof(pgq->quirkname)); 352 mtx_unlock(&usb_quirk_mtx); 353 return (0); /* success */ 354 355 case USB_QUIRK_NAME_GET: 356 pgq = (void *)data; 357 x = pgq->index; 358 if (x >= USB_QUIRK_MAX) { 359 return (EINVAL); 360 } 361 strlcpy(pgq->quirkname, 362 usb_quirkstr(x), sizeof(pgq->quirkname)); 363 return (0); /* success */ 364 365 case USB_DEV_QUIRK_ADD: 366 pgq = (void *)data; 367 368 /* check privileges */ 369 err = priv_check(curthread, PRIV_DRIVER); 370 if (err) { 371 return (err); 372 } 373 /* convert quirk string into numerical */ 374 for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) { 375 if (strcmp(pgq->quirkname, usb_quirkstr(y)) == 0) { 376 break; 377 } 378 } 379 if (y == USB_DEV_QUIRKS_MAX) { 380 return (EINVAL); 381 } 382 if (y == UQ_NONE) { 383 return (EINVAL); 384 } 385 mtx_lock(&usb_quirk_mtx); 386 pqe = usb_quirk_get_entry(pgq->vid, pgq->pid, 387 pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 1); 388 for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { 389 if (pqe->quirks[x] == UQ_NONE) { 390 pqe->quirks[x] = y; 391 break; 392 } 393 } 394 mtx_unlock(&usb_quirk_mtx); 395 if (x == USB_SUB_QUIRKS_MAX) { 396 return (ENOMEM); 397 } 398 return (0); /* success */ 399 400 case USB_DEV_QUIRK_REMOVE: 401 pgq = (void *)data; 402 /* check privileges */ 403 err = priv_check(curthread, PRIV_DRIVER); 404 if (err) { 405 return (err); 406 } 407 /* convert quirk string into numerical */ 408 for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) { 409 if (strcmp(pgq->quirkname, usb_quirkstr(y)) == 0) { 410 break; 411 } 412 } 413 if (y == USB_DEV_QUIRKS_MAX) { 414 return (EINVAL); 415 } 416 if (y == UQ_NONE) { 417 return (EINVAL); 418 } 419 mtx_lock(&usb_quirk_mtx); 420 pqe = usb_quirk_get_entry(pgq->vid, pgq->pid, 421 pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 0); 422 for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { 423 if (pqe->quirks[x] == y) { 424 pqe->quirks[x] = UQ_NONE; 425 break; 426 } 427 } 428 if (x == USB_SUB_QUIRKS_MAX) { 429 mtx_unlock(&usb_quirk_mtx); 430 return (ENOMEM); 431 } 432 for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { 433 if (pqe->quirks[x] != UQ_NONE) { 434 break; 435 } 436 } 437 if (x == USB_SUB_QUIRKS_MAX) { 438 /* all quirk entries are unused - release */ 439 memset(pqe, 0, sizeof(pqe)); 440 } 441 mtx_unlock(&usb_quirk_mtx); 442 return (0); /* success */ 443 444 default: 445 break; 446 } 447 return (ENOIOCTL); 448 } 449 450 static void 451 usb_quirk_init(void *arg) 452 { 453 /* initialize mutex */ 454 mtx_init(&usb_quirk_mtx, "USB quirk", NULL, MTX_DEF); 455 456 /* register our function */ 457 usb_test_quirk_p = &usb_test_quirk_by_info; 458 usb_quirk_ioctl_p = &usb_quirk_ioctl; 459 } 460 461 static void 462 usb_quirk_uninit(void *arg) 463 { 464 usb_quirk_unload(arg); 465 466 /* destroy mutex */ 467 mtx_destroy(&usb_quirk_mtx); 468 } 469 470 SYSINIT(usb_quirk_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_quirk_init, NULL); 471 SYSUNINIT(usb_quirk_uninit, SI_SUB_LOCK, SI_ORDER_ANY, usb_quirk_uninit, NULL); 472