1 /* $NetBSD: usb_quirks.c,v 1.97 2021/02/21 12:36:38 martin Exp $ */ 2 /* $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.30 2003/01/02 04:15:55 imp Exp $ */ 3 4 /* 5 * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Lennart Augustsson (lennart@augustsson.net) at 10 * Carlstedt Research & Technology. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: usb_quirks.c,v 1.97 2021/02/21 12:36:38 martin Exp $"); 36 37 #ifdef _KERNEL_OPT 38 #include "opt_usb.h" 39 #endif 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 44 #include <dev/usb/usb.h> 45 #include <dev/usb/usbdevs.h> 46 #include <dev/usb/usbdi.h> 47 #include <dev/usb/usbdivar.h> 48 #include <dev/usb/usbhist.h> 49 #include <dev/usb/usb_quirks.h> 50 51 #ifdef USB_DEBUG 52 extern int usbdebug; 53 #endif 54 55 #define DPRINTF(FMT,A,B,C,D) USBHIST_LOG(usbdebug,FMT,A,B,C,D) 56 57 #define ANY 0xffff 58 #define _USETW(w) { (w) & 0x00ff, ((w) & 0xff00) >> 8 } 59 60 /* 61 * NXP PN533 NFC chip descriptors 62 */ 63 static const usb_endpoint_descriptor_t desc_ep_pn533_in = { 64 /* bLength */ sizeof(desc_ep_pn533_in), 65 /* bDescriptorType */ UDESC_ENDPOINT, 66 /* bEndpointAddress */ UE_DIR_IN | 0x04, 67 /* bmAttributes */ UE_BULK, 68 /* wMaxPacketSize */ _USETW(0x0040), 69 /* bInterval */ 0x04, /* 255ms */ 70 }; 71 72 static const usb_endpoint_descriptor_t desc_ep_pn533_out = { 73 /* bLength */ sizeof(desc_ep_pn533_in), 74 /* bDescriptorType */ UDESC_ENDPOINT, 75 /* bEndpointAddress */ UE_DIR_OUT | 0x04, 76 /* bmAttributes */ UE_BULK, 77 /* wMaxPacketSize */ _USETW(0x0040), 78 /* bInterval */ 0x04, /* 255ms */ 79 }; 80 81 static const usb_interface_descriptor_t desc_iface_pn533 = { 82 /* bLength */ sizeof(desc_iface_pn533), 83 /* bDescriptorType */ UDESC_INTERFACE, 84 /* bInterfaceNumber */ 0, 85 /* bAlternateSetting */ 0, 86 /* bNumEndpoints */ 2, 87 /* bInterfaceClass */ 0xff, 88 /* bInterfaceSubClass */ 0xff, 89 /* bInterfaceProtocol */ 0xff, 90 /* iInterface */ 0, 91 }; 92 93 static const usb_config_descriptor_t desc_conf_pn533 = { 94 /* bLength */ sizeof(desc_conf_pn533), 95 /* bDescriptorType */ UDESC_CONFIG, 96 /* wTotalLength */ _USETW(sizeof(desc_conf_pn533) + 97 sizeof(desc_iface_pn533) + 98 sizeof(desc_ep_pn533_in) + 99 sizeof(desc_ep_pn533_out) 100 ), 101 /* bNumInterfac */ 1, 102 /* bConfigurationValue */1, 103 /* iConfiguration */ 0, 104 /* bmAttributes */ UC_ATTR_MBO, 105 /* bMaxPower */ 0x32, /* 100mA */ 106 }; 107 108 static const usb_descriptor_t *desc_pn533[] = { 109 (const usb_descriptor_t *)&desc_conf_pn533, 110 (const usb_descriptor_t *)&desc_iface_pn533, 111 (const usb_descriptor_t *)&desc_ep_pn533_out, 112 (const usb_descriptor_t *)&desc_ep_pn533_in, 113 NULL 114 }; 115 116 117 usbd_status 118 usbd_get_desc_fake(struct usbd_device *dev, int type, int index, 119 int len, void *desc) 120 { 121 USBHIST_FUNC(); USBHIST_CALLED(usbdebug); 122 #ifdef USB_DEBUG 123 const usb_device_descriptor_t *dd = usbd_get_device_descriptor(dev); 124 #endif 125 const usb_descriptor_t *ub; 126 int i = 0; 127 int j = 0; 128 usbd_status err = USBD_INVAL; 129 130 if (dev->ud_quirks == NULL || dev->ud_quirks->desc == NULL) { 131 DPRINTF("%04jx/%04j: no fake descriptors", 132 UGETW(dd->idVendor), UGETW(dd->idProduct), 0, 0); 133 goto out; 134 } 135 136 for (j = 0; dev->ud_quirks->desc[j]; j++) { 137 ub = dev->ud_quirks->desc[j]; 138 if (ub->bDescriptorType == type && i++ == index) 139 break; 140 } 141 142 if (dev->ud_quirks->desc[j] == NULL) { 143 DPRINTF("%04jx/%04jx: no fake descriptor type = %jd, len = %jd", 144 UGETW(dd->idVendor), UGETW(dd->idProduct), type, len); 145 goto out; 146 } 147 148 do { 149 ub = dev->ud_quirks->desc[j]; 150 151 if (ub->bLength > len) { 152 DPRINTF("%04jx/%04jx: short buf len = %jd, bLength = %jd", 153 UGETW(dd->idVendor), UGETW(dd->idProduct), 154 type, ub->bLength); 155 goto out; 156 } 157 158 memcpy(desc, ub, ub->bLength); 159 DPRINTF("%04jx/%04jx: Use fake descriptor type %jd", 160 UGETW(dd->idVendor), UGETW(dd->idProduct), 161 type, 0); 162 163 desc = (char *)desc + ub->bLength; 164 len -= ub->bLength; 165 j++; 166 } while (len && dev->ud_quirks->desc[j] && 167 dev->ud_quirks->desc[j]->bDescriptorType != type); 168 169 err = USBD_NORMAL_COMPLETION; 170 171 DPRINTF("%04jx/%04jx: Using fake USB descriptors\n", 172 UGETW(dd->idVendor), UGETW(dd->idProduct), 0, 0); 173 out: 174 DPRINTF("return err = %jd", err, 0, 0, 0); 175 return err; 176 } 177 178 Static const struct usbd_quirk_entry { 179 uint16_t idVendor; 180 uint16_t idProduct; 181 uint16_t bcdDevice; 182 struct usbd_quirks quirks; 183 } usb_quirks[] = { 184 /* Devices which should be ignored by uhid */ 185 { USB_VENDOR_APC, USB_PRODUCT_APC_UPS, ANY, 186 { UQ_HID_IGNORE, NULL }}, 187 { USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_UPS, ANY, 188 { UQ_HID_IGNORE, NULL }}, 189 { USB_VENDOR_GRETAGMACBETH, ANY, ANY, 190 { UQ_HID_IGNORE, NULL }}, 191 { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, ANY, 192 { UQ_HID_IGNORE, NULL }}, 193 { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, ANY, 194 { UQ_HID_IGNORE, NULL }}, 195 { USB_VENDOR_MICROCHIP, USB_PRODUCT_MICROCHIP_PICKIT1, ANY, 196 { UQ_HID_IGNORE, NULL }}, 197 { USB_VENDOR_TRIPPLITE2, ANY, ANY, 198 { UQ_HID_IGNORE, NULL }}, 199 { USB_VENDOR_MISC, USB_PRODUCT_MISC_WISPY_24X, ANY, 200 { UQ_HID_IGNORE, NULL }}, 201 { USB_VENDOR_WELTREND, USB_PRODUCT_WELTREND_HID, ANY, 202 { UQ_HID_IGNORE, NULL }}, 203 { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_EC3, ANY, 204 { UQ_HID_IGNORE, NULL }}, 205 { USB_VENDOR_TI, USB_PRODUCT_TI_MSP430, ANY, 206 { UQ_HID_IGNORE, NULL }}, 207 { USB_VENDOR_XRITE, ANY, ANY, 208 { UQ_HID_IGNORE, NULL }}, 209 { USB_VENDOR_WAYTECH, USB_PRODUCT_WAYTECH_USB2SERIAL, ANY, 210 { UQ_HID_IGNORE, NULL }}, 211 { USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE, 0x100, 212 { UQ_NO_SET_PROTO, NULL }}, 213 { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, 0x094, 214 { UQ_SWAP_UNICODE, NULL }}, 215 { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, 216 { UQ_BAD_ADC, NULL }}, 217 { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, 218 { UQ_AU_NO_XU, NULL }}, 219 { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, 220 { UQ_BAD_ADC, NULL }}, 221 { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, 222 { UQ_BAD_AUDIO, NULL }}, 223 { USB_VENDOR_SONY, USB_PRODUCT_SONY_PS2EYETOY4, 0x000, 224 { UQ_BAD_AUDIO, NULL }}, 225 { USB_VENDOR_SONY, USB_PRODUCT_SONY_PS2EYETOY5, 0x000, 226 { UQ_BAD_AUDIO, NULL }}, 227 { USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_PCVC740K, ANY, 228 { UQ_BAD_AUDIO, NULL }}, 229 { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRONB, 0x000, 230 { UQ_BAD_AUDIO, NULL }}, 231 { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO4K, 0x000, 232 { UQ_BAD_AUDIO, NULL }}, 233 { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMMESS, 0x100, 234 { UQ_BAD_ADC, NULL }}, 235 { USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, 236 { UQ_SPUR_BUT_UP, NULL }}, 237 { USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, 238 { UQ_SPUR_BUT_UP, NULL }}, 239 { USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS, 0x100, 240 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 241 { USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900, 0x000, 242 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 243 { USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_T720C, 0x001, 244 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 245 { USB_VENDOR_EICON, USB_PRODUCT_EICON_DIVA852, 0x100, 246 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 247 { USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_MC75, 0x000, 248 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 249 { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, 250 { UQ_AU_NO_FRAC, NULL }}, 251 { USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE, 0x100, 252 { UQ_AU_INP_ASYNC, NULL }}, 253 { USB_VENDOR_AVANCELOGIC, USB_PRODUCT_AVANCELOGIC_USBAUDIO, 0x101, 254 { UQ_AU_INP_ASYNC, NULL }}, 255 { USB_VENDOR_PLANTRONICS, USB_PRODUCT_PLANTRONICS_HEADSET, 0x004, 256 { UQ_AU_INP_ASYNC, NULL }}, 257 { USB_VENDOR_CMEDIA, USB_PRODUCT_CMEDIA_USBAUDIO, ANY, 258 { UQ_AU_INP_ASYNC, NULL }}, 259 260 /* XXX These should have a revision number, but I don't know what they are. */ 261 { USB_VENDOR_HP, USB_PRODUCT_HP_895C, ANY, 262 { UQ_BROKEN_BIDIR, NULL }}, 263 { USB_VENDOR_HP, USB_PRODUCT_HP_880C, ANY, 264 { UQ_BROKEN_BIDIR, NULL }}, 265 { USB_VENDOR_HP, USB_PRODUCT_HP_815C, ANY, 266 { UQ_BROKEN_BIDIR, NULL }}, 267 { USB_VENDOR_HP, USB_PRODUCT_HP_810C, ANY, 268 { UQ_BROKEN_BIDIR, NULL }}, 269 { USB_VENDOR_HP, USB_PRODUCT_HP_830C, ANY, 270 { UQ_BROKEN_BIDIR, NULL }}, 271 { USB_VENDOR_HP, USB_PRODUCT_HP_885C, ANY, 272 { UQ_BROKEN_BIDIR, NULL }}, 273 { USB_VENDOR_HP, USB_PRODUCT_HP_840C, ANY, 274 { UQ_BROKEN_BIDIR, NULL }}, 275 { USB_VENDOR_HP, USB_PRODUCT_HP_816C, ANY, 276 { UQ_BROKEN_BIDIR, NULL }}, 277 { USB_VENDOR_HP, USB_PRODUCT_HP_959C, ANY, 278 { UQ_BROKEN_BIDIR, NULL }}, 279 { USB_VENDOR_MTK, USB_PRODUCT_MTK_GPS_RECEIVER, ANY, 280 { UQ_NO_UNION_NRM, NULL }}, 281 { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY900, ANY, 282 { UQ_BROKEN_BIDIR, NULL }}, 283 { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY760, ANY, 284 { UQ_BROKEN_BIDIR, NULL }}, 285 { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY920, ANY, 286 { UQ_BROKEN_BIDIR, NULL }}, 287 { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY800, ANY, 288 { UQ_BROKEN_BIDIR, NULL }}, 289 { USB_VENDOR_HP, USB_PRODUCT_HP_1220C, ANY, 290 { UQ_BROKEN_BIDIR, NULL }}, 291 292 /* Apple internal notebook ISO keyboards have swapped keys */ 293 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_FOUNTAIN_ISO, ANY, 294 { UQ_APPLE_ISO, NULL }}, 295 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER_ISO, ANY, 296 { UQ_APPLE_ISO, NULL }}, 297 298 /* HID and audio are both invalid on iPhone/iPod Touch */ 299 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, ANY, 300 { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, 301 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH, ANY, 302 { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, 303 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_4G, ANY, 304 { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, 305 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, ANY, 306 { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, 307 { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, ANY, 308 { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, 309 310 /* 311 * Various devices using serial boot loader protocol, as supported 312 * by pkgsrc/sysutils/imx_usb_loader 313 */ 314 { 0x066f, 0x3780, /* mx23 */ ANY, 315 { UQ_HID_IGNORE, NULL }}, 316 { 0x15a2, 0x004f, /* mx28 */ ANY, 317 { UQ_HID_IGNORE, NULL }}, 318 { 0x15a2, 0x0052, /* mx50 */ ANY, 319 { UQ_HID_IGNORE, NULL }}, 320 { 0x15a2, 0x0054, /* mx6 */ ANY, 321 { UQ_HID_IGNORE, NULL }}, 322 { 0x15a2, 0x0061, /* mx6 */ ANY, 323 { UQ_HID_IGNORE, NULL }}, 324 { 0x15a2, 0x0063, /* mx6 */ ANY, 325 { UQ_HID_IGNORE, NULL }}, 326 { 0x15a2, 0x0071, /* mx6 */ ANY, 327 { UQ_HID_IGNORE, NULL }}, 328 { 0x15a2, 0x007d, /* mx6 */ ANY, 329 { UQ_HID_IGNORE, NULL }}, 330 { 0x15a2, 0x0080, /* mx6ull */ ANY, 331 { UQ_HID_IGNORE, NULL }}, 332 { 0x1fc9, 0x0128, /* mx6 */ ANY, 333 { UQ_HID_IGNORE, NULL }}, 334 { 0x15a2, 0x0076, /* mx7 */ ANY, 335 { UQ_HID_IGNORE, NULL }}, 336 { 0x1fc9, 0x0126, /* mx7ulp */ ANY, 337 { UQ_HID_IGNORE, NULL }}, 338 { 0x15a2, 0x0041, /* mx51 */ ANY, 339 { UQ_HID_IGNORE, NULL }}, 340 { 0x15a2, 0x004e, /* mx53 */ ANY, 341 { UQ_HID_IGNORE, NULL }}, 342 { 0x15a2, 0x006a, /* vybrid */ ANY, 343 { UQ_HID_IGNORE, NULL }}, 344 { 0x066f, 0x37ff, /* linux_gadget */ ANY, 345 { UQ_HID_IGNORE, NULL }}, 346 { 0x1b67, 0x4fff, /* mx6 */ ANY, 347 { UQ_HID_IGNORE, NULL }}, 348 { 0x0525, 0xb4a4, /* mx6 */ ANY, 349 { UQ_HID_IGNORE, NULL }}, 350 { 0x1fc9, 0x012b, /* mx8mq */ ANY, 351 { UQ_HID_IGNORE, NULL }}, 352 { 0x1fc9, 0x0134, /* mx8mm */ ANY, 353 { UQ_HID_IGNORE, NULL }}, 354 { 0x1fc9, 0x013e, /* mx8mn */ ANY, 355 { UQ_HID_IGNORE, NULL }}, 356 { 0x3016, 0x1001, /* mx8mn */ ANY, 357 { UQ_HID_IGNORE, NULL }}, 358 359 { USB_VENDOR_LG, USB_PRODUCT_LG_CDMA_MSM, ANY, 360 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 361 { USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_CDMA_MSM, ANY, 362 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 363 { USB_VENDOR_HYUNDAI, USB_PRODUCT_HYUNDAI_UM175, ANY, 364 { UQ_ASSUME_CM_OVER_DATA, NULL }}, 365 { USB_VENDOR_ZOOM, USB_PRODUCT_ZOOM_3095, ANY, 366 { UQ_LOST_CS_DESC, NULL }}, 367 368 /* 369 * NXP PN533 bugs 370 * 371 * 1. It corrupts its USB descriptors. The quirk is to provide hardcoded 372 * descriptors instead of getting them from the device. 373 * 2. It mishandles the USB toggle bit. This causes some replies to be 374 * filered out by the USB host controller and be reported as timed out. 375 * NFC tool's libnfc workaround this bug by sending a dummy frame to 376 * resync the toggle bit, but in order to succeed, that operation must 377 * not be reported as failed. The quirk is therefore to pretend to 378 * userland that output timeouts are successes. 379 */ 380 { USB_VENDOR_PHILIPSSEMI, USB_PRODUCT_PHILIPSSEMI_PN533, ANY, 381 { UQ_DESC_CORRUPT | UQ_MISS_OUT_ACK, desc_pn533 }}, 382 { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SCL3711, ANY, 383 { UQ_DESC_CORRUPT | UQ_MISS_OUT_ACK, desc_pn533 }}, 384 { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SCL3712, ANY, 385 { UQ_DESC_CORRUPT | UQ_MISS_OUT_ACK, desc_pn533 }}, 386 { 0, 0, 0, { 0, NULL } } 387 }; 388 389 const struct usbd_quirks usbd_no_quirk = { 0 }; 390 391 const struct usbd_quirks * 392 usbd_find_quirk(usb_device_descriptor_t *d) 393 { 394 const struct usbd_quirk_entry *t; 395 uint16_t vendor = UGETW(d->idVendor); 396 uint16_t product = UGETW(d->idProduct); 397 uint16_t revision = UGETW(d->bcdDevice); 398 399 for (t = usb_quirks; t->idVendor != 0; t++) { 400 if (t->idVendor == vendor && 401 (t->idProduct == ANY || t->idProduct == product) && 402 (t->bcdDevice == ANY || t->bcdDevice == revision)) 403 break; 404 } 405 #ifdef USB_DEBUG 406 if (usbdebug && t->quirks.uq_flags) 407 printf("usbd_find_quirk 0x%04x/0x%04x/%x: %d\n", 408 UGETW(d->idVendor), UGETW(d->idProduct), 409 UGETW(d->bcdDevice), t->quirks.uq_flags); 410 #endif 411 return &t->quirks; 412 } 413