/* $NetBSD: usb_quirks.c,v 1.108 2024/02/28 21:52:40 dholland Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.30 2003/01/02 04:15:55 imp Exp $ */ /* * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 __KERNEL_RCSID(0, "$NetBSD: usb_quirks.c,v 1.108 2024/02/28 21:52:40 dholland Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" #endif #include #include #include #include #include #include #include #include #define DPRINTF(FMT,A,B,C,D) USBHIST_LOG(usbdebug,FMT,A,B,C,D) #define ANY 0xffff #define _USETW(w) { (w) & 0x00ff, ((w) & 0xff00) >> 8 } /* * NXP PN533 NFC chip descriptors */ static const usb_endpoint_descriptor_t desc_ep_pn533_in = { /* bLength */ sizeof(desc_ep_pn533_in), /* bDescriptorType */ UDESC_ENDPOINT, /* bEndpointAddress */ UE_DIR_IN | 0x04, /* bmAttributes */ UE_BULK, /* wMaxPacketSize */ _USETW(0x0040), /* bInterval */ 0x04, /* 255ms */ }; static const usb_endpoint_descriptor_t desc_ep_pn533_out = { /* bLength */ sizeof(desc_ep_pn533_in), /* bDescriptorType */ UDESC_ENDPOINT, /* bEndpointAddress */ UE_DIR_OUT | 0x04, /* bmAttributes */ UE_BULK, /* wMaxPacketSize */ _USETW(0x0040), /* bInterval */ 0x04, /* 255ms */ }; static const usb_interface_descriptor_t desc_iface_pn533 = { /* bLength */ sizeof(desc_iface_pn533), /* bDescriptorType */ UDESC_INTERFACE, /* bInterfaceNumber */ 0, /* bAlternateSetting */ 0, /* bNumEndpoints */ 2, /* bInterfaceClass */ 0xff, /* bInterfaceSubClass */ 0xff, /* bInterfaceProtocol */ 0xff, /* iInterface */ 0, }; static const usb_config_descriptor_t desc_conf_pn533 = { /* bLength */ sizeof(desc_conf_pn533), /* bDescriptorType */ UDESC_CONFIG, /* wTotalLength */ _USETW(sizeof(desc_conf_pn533) + sizeof(desc_iface_pn533) + sizeof(desc_ep_pn533_in) + sizeof(desc_ep_pn533_out) ), /* bNumInterface */ 1, /* bConfigurationValue */1, /* iConfiguration */ 0, /* bmAttributes */ UC_ATTR_MBO, /* bMaxPower */ 0x32, /* 100mA */ }; static const usb_descriptor_t *desc_pn533[] = { (const usb_descriptor_t *)&desc_conf_pn533, (const usb_descriptor_t *)&desc_iface_pn533, (const usb_descriptor_t *)&desc_ep_pn533_out, (const usb_descriptor_t *)&desc_ep_pn533_in, NULL }; usbd_status usbd_get_desc_fake(struct usbd_device *dev, int type, int index, int len, void *desc) { USBHIST_FUNC(); USBHIST_CALLED(usbdebug); #ifdef USB_DEBUG const usb_device_descriptor_t *dd = usbd_get_device_descriptor(dev); #endif const usb_descriptor_t *ub; int i = 0; int j = 0; usbd_status err = USBD_INVAL; if (dev->ud_quirks == NULL || dev->ud_quirks->desc == NULL) { DPRINTF("%04jx/%04j: no fake descriptors", UGETW(dd->idVendor), UGETW(dd->idProduct), 0, 0); goto out; } for (j = 0; dev->ud_quirks->desc[j]; j++) { ub = dev->ud_quirks->desc[j]; if (ub->bDescriptorType == type && i++ == index) break; } if (dev->ud_quirks->desc[j] == NULL) { DPRINTF("%04jx/%04jx: no fake descriptor type = %jd, len = %jd", UGETW(dd->idVendor), UGETW(dd->idProduct), type, len); goto out; } do { ub = dev->ud_quirks->desc[j]; if (ub->bLength > len) { DPRINTF("%04jx/%04jx: short buf len = %jd, bLength = %jd", UGETW(dd->idVendor), UGETW(dd->idProduct), type, ub->bLength); goto out; } memcpy(desc, ub, ub->bLength); DPRINTF("%04jx/%04jx: Use fake descriptor type %jd", UGETW(dd->idVendor), UGETW(dd->idProduct), type, 0); desc = (char *)desc + ub->bLength; len -= ub->bLength; j++; } while (len && dev->ud_quirks->desc[j] && dev->ud_quirks->desc[j]->bDescriptorType != type); err = USBD_NORMAL_COMPLETION; DPRINTF("%04jx/%04jx: Using fake USB descriptors\n", UGETW(dd->idVendor), UGETW(dd->idProduct), 0, 0); out: DPRINTF("return err = %jd", err, 0, 0, 0); return err; } Static const struct usbd_quirk_entry { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; struct usbd_quirks quirks; } usb_quirks[] = { /* Devices which should be ignored by uhid */ { USB_VENDOR_APC, USB_PRODUCT_APC_UPS, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_APC, USB_PRODUCT_APC_UPS3, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_UPS0, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_UPS, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_UPS2, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_GRETAGMACBETH, ANY, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_MICROCHIP, USB_PRODUCT_MICROCHIP_PICKIT1, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_MICROCHIP, USB_PRODUCT_MICROCHIP_PICKIT2, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_MICROCHIP, USB_PRODUCT_MICROCHIP_PICKIT3, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_TRIPPLITE2, ANY, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_MISC, USB_PRODUCT_MISC_WISPY_24X, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_WELTREND, USB_PRODUCT_WELTREND_HID, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_EC3, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_TI, USB_PRODUCT_TI_MSP430, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_XRITE, ANY, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_WAYTECH, USB_PRODUCT_WAYTECH_USB2SERIAL, ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE, 0x100, { UQ_NO_SET_PROTO, NULL }}, { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, 0x094, { UQ_SWAP_UNICODE, NULL }}, { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_BAD_ADC, NULL }}, { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_AU_NO_XU, NULL }}, { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, { UQ_BAD_ADC, NULL }}, { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, { UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_SONY, USB_PRODUCT_SONY_PS2EYETOY4, 0x000, { UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_SONY, USB_PRODUCT_SONY_PS2EYETOY5, 0x000, { UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_PCVC740K, ANY, { UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRONB, 0x000, { UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO4K, 0x000, { UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMMESS, 0x100, { UQ_BAD_ADC, NULL }}, { USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, { UQ_SPUR_BUT_UP, NULL }}, { USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, { UQ_SPUR_BUT_UP, NULL }}, { USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS, 0x100, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900, 0x000, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_T720C, 0x001, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_EICON, USB_PRODUCT_EICON_DIVA852, 0x100, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_MC75, 0x000, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, { UQ_AU_NO_FRAC, NULL }}, { USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE, 0x100, { UQ_AU_INP_ASYNC, NULL }}, { USB_VENDOR_AVANCELOGIC, USB_PRODUCT_AVANCELOGIC_USBAUDIO, 0x101, { UQ_AU_INP_ASYNC, NULL }}, { USB_VENDOR_PLANTRONICS, USB_PRODUCT_PLANTRONICS_HEADSET, 0x004, { UQ_AU_INP_ASYNC, NULL }}, { USB_VENDOR_CMEDIA, USB_PRODUCT_CMEDIA_USBAUDIO, ANY, { UQ_AU_INP_ASYNC, NULL }}, /* XXX These should have a revision number, but I don't know what they are. */ { USB_VENDOR_HP, USB_PRODUCT_HP_895C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_880C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_815C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_810C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_830C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_885C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_840C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_816C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_959C, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_MTK, USB_PRODUCT_MTK_GPS_RECEIVER, ANY, { UQ_NO_UNION_NRM, NULL }}, { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY900, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY760, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY920, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_NEC, USB_PRODUCT_NEC_PICTY800, ANY, { UQ_BROKEN_BIDIR, NULL }}, { USB_VENDOR_HP, USB_PRODUCT_HP_1220C, ANY, { UQ_BROKEN_BIDIR, NULL }}, /* Apple internal notebook ISO keyboards have swapped keys */ { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_FOUNTAIN_ISO, ANY, { UQ_APPLE_ISO, NULL }}, { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER_ISO, ANY, { UQ_APPLE_ISO, NULL }}, /* HID and audio are both invalid on iPhone/iPod Touch */ { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, ANY, { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH, ANY, { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_4G, ANY, { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, ANY, { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, ANY, { UQ_HID_IGNORE | UQ_BAD_AUDIO, NULL }}, /* * Various devices using serial boot loader protocol, as supported * by pkgsrc/sysutils/imx_usb_loader */ { 0x066f, 0x3780, /* mx23 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x004f, /* mx28 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0052, /* mx50 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0054, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0061, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0063, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0071, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x007d, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0080, /* mx6ull */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x1fc9, 0x0128, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0076, /* mx7 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x1fc9, 0x0126, /* mx7ulp */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x0041, /* mx51 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x004e, /* mx53 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x15a2, 0x006a, /* vybrid */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x066f, 0x37ff, /* linux_gadget */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x1b67, 0x4fff, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x0525, 0xb4a4, /* mx6 */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x1fc9, 0x012b, /* mx8mq */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x1fc9, 0x0134, /* mx8mm */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x1fc9, 0x013e, /* mx8mn */ ANY, { UQ_HID_IGNORE, NULL }}, { 0x3016, 0x1001, /* mx8mn */ ANY, { UQ_HID_IGNORE, NULL }}, { USB_VENDOR_LG, USB_PRODUCT_LG_CDMA_MSM, ANY, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_CDMA_MSM, ANY, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_HYUNDAI, USB_PRODUCT_HYUNDAI_UM175, ANY, { UQ_ASSUME_CM_OVER_DATA, NULL }}, { USB_VENDOR_ZOOM, USB_PRODUCT_ZOOM_3095, ANY, { UQ_LOST_CS_DESC, NULL }}, /* * NXP PN533 bugs * * 1. It corrupts its USB descriptors. The quirk is to provide hardcoded * descriptors instead of getting them from the device. * 2. It mishandles the USB toggle bit. This causes some replies to be * filtered out by the USB host controller and be reported as timed out. * NFC tool's libnfc workaround this bug by sending a dummy frame to * resync the toggle bit, but in order to succeed, that operation must * not be reported as failed. The quirk is therefore to pretend to * userland that output timeouts are successes. */ { USB_VENDOR_PHILIPSSEMI, USB_PRODUCT_PHILIPSSEMI_PN533, ANY, { UQ_DESC_CORRUPT | UQ_MISS_OUT_ACK, desc_pn533 }}, { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SCL3711, ANY, { UQ_DESC_CORRUPT | UQ_MISS_OUT_ACK, desc_pn533 }}, { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SCL3712, ANY, { UQ_DESC_CORRUPT | UQ_MISS_OUT_ACK, desc_pn533 }}, /* * These cheap mice will disconnect after 60 seconds, * reconnect, and then disconnect again (ad nauseum) * unless it's kept open. */ { USB_VENDOR_CHICONY, USB_PRODUCT_CHICONY_OPTMOUSE0939, ANY, { UQ_ALWAYS_ON, NULL }}, { USB_VENDOR_PIXART, USB_PRODUCT_PIXART_RPIMOUSE, ANY, { UQ_ALWAYS_ON, NULL }}, { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_B100_1, ANY, { UQ_ALWAYS_ON, NULL }}, { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_B100_2, ANY, { UQ_ALWAYS_ON, NULL }}, { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_OPTUSBMOUSE, ANY, { UQ_ALWAYS_ON, NULL }}, /* * The HAILUCK USB Keyboard has a built-in touchpad, which * needs to be active for the keyboard to function properly. */ { USB_VENDOR_HAILUCK, USB_PRODUCT_HAILUCK_KEYBOARD, ANY, { UQ_ALWAYS_ON, NULL }}, { 0, 0, 0, { 0, NULL } } }; const struct usbd_quirks usbd_no_quirk = { 0 }; const struct usbd_quirks * usbd_find_quirk(usb_device_descriptor_t *d) { const struct usbd_quirk_entry *t; uint16_t vendor = UGETW(d->idVendor); uint16_t product = UGETW(d->idProduct); uint16_t revision = UGETW(d->bcdDevice); for (t = usb_quirks; t->idVendor != 0; t++) { if (t->idVendor == vendor && (t->idProduct == ANY || t->idProduct == product) && (t->bcdDevice == ANY || t->bcdDevice == revision)) break; } #ifdef USB_DEBUG if (usbdebug && t->quirks.uq_flags) printf("usbd_find_quirk 0x%04x/0x%04x/%x: %d\n", UGETW(d->idVendor), UGETW(d->idProduct), UGETW(d->bcdDevice), t->quirks.uq_flags); #endif return &t->quirks; }