diff options
author | Nadia Holmquist Pedersen | 2020-06-11 18:20:15 +0200 |
---|---|---|
committer | Nadia Holmquist Pedersen | 2020-06-11 18:20:15 +0200 |
commit | 564dfbc25aaddcd2b2ca28c4de731de5394f68e7 (patch) | |
tree | b67e038f9cd3b2e03ac3101bd1ea5076491b6a72 | |
download | aur-564dfbc25aaddcd2b2ca28c4de731de5394f68e7.tar.gz |
Initial commit
-rw-r--r-- | .SRCINFO | 19 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | PKGBUILD | 26 | ||||
-rw-r--r-- | dkms.conf | 8 | ||||
-rw-r--r-- | hid-ids.h | 1281 | ||||
-rw-r--r-- | hid-nintendo.c | 1971 |
6 files changed, 3317 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 000000000000..0deb80802df0 --- /dev/null +++ b/.SRCINFO @@ -0,0 +1,19 @@ +pkgbase = hid-nintendo-nso-dkms + pkgdesc = HID driver for Nintendo Switch controllers patched with Switch Online NES and SNES controller support. + pkgver = 1.1 + pkgrel = 2 + url = https://github.com/nadiaholmquist/linux/tree/hid-nintendo + arch = x86_64 + arch = aarch64 + license = GPL2 + depends = dkms + conflicts = hid-nintendo-dkms + source = hid-nintendo.c + source = dkms.conf + source = hid-ids.h + md5sums = dbc0bd6f144503547cb6e140c1ea2729 + md5sums = 6d97239c33773b3f2fc5d497e98a1017 + md5sums = 6d1c428af9d73b4fd493ee1d4465700b + +pkgname = hid-nintendo-nso-dkms + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000000..a75766702c7f --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +obj-m += hid-nintendo.o + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) V=0 M=$$PWD + +install: + cp hid-nintendo.ko $(DESTDIR)/ + +clean: + rm -rf deps.h *.o *.ko *.mod.c *.symvers *.order .*.cmd .tmp_versions diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000000..afbac60b47cd --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,26 @@ + +_pkgbase=hid-nintendo +pkgname=hid-nintendo-nso-dkms +pkgver=1.1 +pkgrel=2 +pkgdesc="HID driver for Nintendo Switch controllers patched with Switch Online NES and SNES controller support." +arch=('x86_64' 'aarch64') +url="https://github.com/nadiaholmquist/linux/tree/hid-nintendo" +license=("GPL2") +depends=('dkms') +source=('hid-nintendo.c' 'dkms.conf' 'hid-ids.h') +conflicts=(hid-nintendo-dkms) + +md5sums=('dbc0bd6f144503547cb6e140c1ea2729' + '6d97239c33773b3f2fc5d497e98a1017' + '6d1c428af9d73b4fd493ee1d4465700b') + +package() { + install -Dm644 dkms.conf "${pkgdir}"/usr/src/${_pkgbase}-${pkgver}/dkms.conf + sed -e "s/@_PKGBASE@/${_pkgbase}/" \ + -e "s/@PKGVER@/${pkgver}/" \ + -i "${pkgdir}"/usr/src/${_pkgbase}-${pkgver}/dkms.conf + install -Dm644 hid-nintendo.c "${pkgdir}"/usr/src/${_pkgbase}-${pkgver}/hid-nintendo.c + install -Dm644 hid-ids.h "${pkgdir}"/usr/src/${_pkgbase}-${pkgver}/hid-ids.h + install -Dm644 Makefile "${pkgdir}"/usr/src/${_pkgbase}-${pkgver}/Makefile +} diff --git a/dkms.conf b/dkms.conf new file mode 100644 index 000000000000..58dec07cc80a --- /dev/null +++ b/dkms.conf @@ -0,0 +1,8 @@ +PACKAGE_NAME="@_PKGBASE@" +PACKAGE_VERSION="@PKGVER@" +MAKE="make -C $kernel_source_dir M=$dkms_tree/$PACKAGE_NAME/$PACKAGE_VERSION/build/ modules" +CLEAN="make -C $kernel_source_dir M+$dkms_tree/$PACKAGE_NAME/$PACKAGE_VERSION/build/ clean" +BUILT_MODULE_NAME[0]="@_PKGBASE@" +BUILT_MODULE_LOCATION[0]="" +DEST_MODULE_LOCATION[0]="/kernel/drivers/hid/" +AUTOINSTALL="yes" diff --git a/hid-ids.h b/hid-ids.h new file mode 100644 index 000000000000..484a2f1ff47c --- /dev/null +++ b/hid-ids.h @@ -0,0 +1,1281 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * USB HID quirks support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + */ + +/* + */ + +#ifndef HID_IDS_H_FILE +#define HID_IDS_H_FILE + +#define USB_VENDOR_ID_258A 0x258a +#define USB_DEVICE_ID_258A_6A88 0x6a88 + +#define USB_VENDOR_ID_3M 0x0596 +#define USB_DEVICE_ID_3M1968 0x0500 +#define USB_DEVICE_ID_3M2256 0x0502 +#define USB_DEVICE_ID_3M3266 0x0506 + +#define USB_VENDOR_ID_A4TECH 0x09da +#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 +#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a +#define USB_DEVICE_ID_A4TECH_RP_649 0x001a + +#define USB_VENDOR_ID_AASHIMA 0x06d6 +#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 +#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + +#define USB_VENDOR_ID_ACRUX 0x1a34 + +#define USB_VENDOR_ID_ACTIONSTAR 0x2101 +#define USB_DEVICE_ID_ACTIONSTAR_1011 0x1011 + +#define USB_VENDOR_ID_ADS_TECH 0x06e1 +#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155 + +#define USB_VENDOR_ID_AFATECH 0x15a4 +#define USB_DEVICE_ID_AFATECH_AF9016 0x9016 + +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_DEVICE_ID_AIPTEK_01 0x0001 +#define USB_DEVICE_ID_AIPTEK_10 0x0010 +#define USB_DEVICE_ID_AIPTEK_20 0x0020 +#define USB_DEVICE_ID_AIPTEK_21 0x0021 +#define USB_DEVICE_ID_AIPTEK_22 0x0022 +#define USB_DEVICE_ID_AIPTEK_23 0x0023 +#define USB_DEVICE_ID_AIPTEK_24 0x0024 + +#define USB_VENDOR_ID_AIRCABLE 0x16CA +#define USB_DEVICE_ID_AIRCABLE1 0x1502 + +#define USB_VENDOR_ID_AIREN 0x1a2c +#define USB_DEVICE_ID_AIREN_SLIMPLUS 0x0002 + +#define USB_VENDOR_ID_AKAI 0x2011 +#define USB_DEVICE_ID_AKAI_MPKMINI2 0x0715 + +#define USB_VENDOR_ID_AKAI_09E8 0x09E8 +#define USB_DEVICE_ID_AKAI_09E8_MIDIMIX 0x0031 + +#define USB_VENDOR_ID_ALCOR 0x058f +#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 +#define USB_DEVICE_ID_ALCOR_MALTRON_KB 0x9410 + +#define USB_VENDOR_ID_ALPS 0x0433 +#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 + +#define USB_VENDOR_ID_ALPS_JP 0x044E +#define HID_DEVICE_ID_ALPS_U1_DUAL 0x120B +#define HID_DEVICE_ID_ALPS_U1_DUAL_PTP 0x121F +#define HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP 0x1220 +#define HID_DEVICE_ID_ALPS_U1 0x1215 +#define HID_DEVICE_ID_ALPS_T4_BTNLESS 0x120C +#define HID_DEVICE_ID_ALPS_1222 0x1222 + + +#define USB_VENDOR_ID_AMI 0x046b +#define USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE 0xff10 + +#define USB_VENDOR_ID_ANTON 0x1130 +#define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101 + +#define USB_VENDOR_ID_APPLE 0x05ac +#define BT_VENDOR_ID_APPLE 0x004c +#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 +#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d +#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e +#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 0x0265 +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f +#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 +#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 +#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 +#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 +#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 +#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 +#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a +#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b +#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c +#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d +#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e +#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f +#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 +#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 +#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 +#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229 +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240 +#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 +#define USB_DEVICE_ID_APPLE_ALU_REVB_ANSI 0x024f +#define USB_DEVICE_ID_APPLE_ALU_REVB_ISO 0x0250 +#define USB_DEVICE_ID_APPLE_ALU_REVB_JIS 0x0251 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254 +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259 +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249 +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d +#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e +#define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI 0x0262 +#define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO 0x0263 +#define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS 0x0264 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257 +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI 0x0267 +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_ANSI 0x026c +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272 +#define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273 +#define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274 +#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a +#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 +#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 + +#define USB_VENDOR_ID_ASUS 0x0486 +#define USB_DEVICE_ID_ASUS_T91MT 0x0185 +#define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186 + +#define USB_VENDOR_ID_ASUSTEK 0x0b05 +#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726 +#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b +#define USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD 0x17e0 +#define USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD 0x1807 +#define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD 0x8502 +#define USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD 0x183d +#define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD 0x184a +#define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585 +#define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD 0x0101 +#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854 +#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837 +#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822 +#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 + +#define USB_VENDOR_ID_ATEN 0x0557 +#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 +#define USB_DEVICE_ID_ATEN_CS124U 0x2202 +#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 +#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 +#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 +#define USB_DEVICE_ID_ATEN_CS682 0x2213 +#define USB_DEVICE_ID_ATEN_CS692 0x8021 +#define USB_DEVICE_ID_ATEN_CS1758 0x2220 + +#define USB_VENDOR_ID_ATMEL 0x03eb +#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c +#define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118 +#define USB_VENDOR_ID_ATMEL_V_USB 0x16c0 +#define USB_DEVICE_ID_ATMEL_V_USB 0x05df + +#define USB_VENDOR_ID_AUREAL 0x0755 +#define USB_DEVICE_ID_AUREAL_W01RN 0x2626 + +#define USB_VENDOR_ID_AVERMEDIA 0x07ca +#define USB_DEVICE_ID_AVER_FM_MR800 0xb800 + +#define USB_VENDOR_ID_AXENTIA 0x12cf +#define USB_DEVICE_ID_AXENTIA_FM_RADIO 0x7111 + +#define USB_VENDOR_ID_BAANTO 0x2453 +#define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100 + +#define USB_VENDOR_ID_BELKIN 0x050d +#define USB_DEVICE_ID_FLIP_KVM 0x3201 + +#define USB_VENDOR_ID_BERKSHIRE 0x0c98 +#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 + +#define USB_VENDOR_ID_BETOP_2185BFM 0x11c2 +#define USB_VENDOR_ID_BETOP_2185PC 0x11c0 +#define USB_VENDOR_ID_BETOP_2185V2PC 0x8380 +#define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc + +#define USB_VENDOR_ID_BIGBEN 0x146b +#define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD 0x0902 + +#define USB_VENDOR_ID_BTC 0x046e +#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 +#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 + +#define USB_VENDOR_ID_CANDO 0x2087 +#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1 0x0a02 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6 0x0f01 + +#define USB_VENDOR_ID_CH 0x068e +#define USB_DEVICE_ID_CH_PRO_THROTTLE 0x00f1 +#define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2 +#define USB_DEVICE_ID_CH_FIGHTERSTICK 0x00f3 +#define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4 +#define USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE 0x0051 +#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff +#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3 +#define USB_DEVICE_ID_CH_AXIS_295 0x001c + +#define USB_VENDOR_ID_CHERRY 0x046a +#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 +#define USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR 0x0027 + +#define USB_VENDOR_ID_CHIC 0x05fe +#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 + +#define USB_VENDOR_ID_CHICONY 0x04f2 +#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 +#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d +#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618 +#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053 +#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE2 0x0939 +#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123 +#define USB_DEVICE_ID_ASUS_AK1D 0x1125 +#define USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A 0x1408 +#define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421 + +#define USB_VENDOR_ID_CHUNGHWAT 0x2247 +#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001 + +#define USB_VENDOR_ID_CIDC 0x1677 + +#define I2C_VENDOR_ID_CIRQUE 0x0488 +#define I2C_PRODUCT_ID_CIRQUE_121F 0x121F + +#define USB_VENDOR_ID_CJTOUCH 0x24b8 +#define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020 +#define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0040 0x0040 + +#define USB_VENDOR_ID_CMEDIA 0x0d8c +#define USB_DEVICE_ID_CM109 0x000e +#define USB_DEVICE_ID_CM6533 0x0022 + +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff + +#define USB_VENDOR_ID_CORSAIR 0x1b1c +#define USB_DEVICE_ID_CORSAIR_K90 0x1b02 + +#define USB_VENDOR_ID_CORSAIR 0x1b1c +#define USB_DEVICE_ID_CORSAIR_K70R 0x1b09 +#define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11 +#define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12 +#define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13 +#define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15 +#define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17 +#define USB_DEVICE_ID_CORSAIR_GLAIVE_RGB 0x1b34 +#define USB_DEVICE_ID_CORSAIR_K70RGB_RAPIDFIRE 0x1b38 +#define USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE 0x1b39 +#define USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB 0x1b3e + +#define USB_VENDOR_ID_CREATIVELABS 0x041e +#define USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51 0x322c +#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801 +#define USB_DEVICE_ID_CREATIVE_SB0540 0x3100 + +#define USB_VENDOR_ID_CVTOUCH 0x1ff7 +#define USB_DEVICE_ID_CVTOUCH_SCREEN 0x0013 + +#define USB_VENDOR_ID_CYGNAL 0x10c4 +#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a +#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH 0x81b9 +#define USB_DEVICE_ID_CYGNAL_CP2112 0xea90 +#define USB_DEVICE_ID_U2F_ZERO 0x8acf + +#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244 + +#define USB_VENDOR_ID_CYPRESS 0x04b4 +#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 +#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 +#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 +#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 +#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 +#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1 +#define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81 +#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001 + +#define USB_VENDOR_ID_DATA_MODUL 0x7374 +#define USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH 0x1201 + +#define USB_VENDOR_ID_DEALEXTREAME 0x10c5 +#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a + +#define USB_VENDOR_ID_DELCOM 0x0fc5 +#define USB_DEVICE_ID_DELCOM_VISUAL_IND 0xb080 + +#define USB_VENDOR_ID_DELL 0x413c +#define USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE 0x301a + +#define USB_VENDOR_ID_DELORME 0x1163 +#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 +#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 + +#define USB_VENDOR_ID_DMI 0x0c0b +#define USB_DEVICE_ID_DMI_ENC 0x5fab + +#define USB_VENDOR_ID_DRAGONRISE 0x0079 +#define USB_DEVICE_ID_REDRAGON_SEYMUR2 0x0006 +#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800 +#define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801 +#define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803 +#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE1 0x1843 +#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE2 0x1844 + +#define USB_VENDOR_ID_DWAV 0x0eef +#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 +#define USB_DEVICE_ID_DWAV_TOUCHCONTROLLER 0x0002 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A 0x722A +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 + +#define USB_VENDOR_ID_ELAN 0x04f3 +#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 +#define USB_DEVICE_ID_HP_X2 0x074d +#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 + +#define USB_VENDOR_ID_ELECOM 0x056e +#define USB_DEVICE_ID_ELECOM_BM084 0x0061 +#define USB_DEVICE_ID_ELECOM_M_XT3URBK 0x00fb +#define USB_DEVICE_ID_ELECOM_M_XT3DRBK 0x00fc +#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd +#define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe +#define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff +#define USB_DEVICE_ID_ELECOM_M_HT1URBK 0x010c +#define USB_DEVICE_ID_ELECOM_M_HT1DRBK 0x010d + +#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 +#define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004 +#define USB_DEVICE_ID_DREAM_CHEEKY_FA 0x000a + +#define USB_VENDOR_ID_ELITEGROUP 0x03fc +#define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8 + +#define USB_VENDOR_ID_ELO 0x04E7 +#define USB_DEVICE_ID_ELO_TS2515 0x0022 +#define USB_DEVICE_ID_ELO_TS2700 0x0020 +#define USB_DEVICE_ID_ELO_ACCUTOUCH_2216 0x0050 + +#define USB_VENDOR_ID_EMS 0x2006 +#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 + +#define USB_VENDOR_ID_FLATFROG 0x25b5 +#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002 + +#define USB_VENDOR_ID_FUTABA 0x0547 +#define USB_DEVICE_ID_LED_DISPLAY 0x7000 + +#define USB_VENDOR_ID_FUTURE_TECHNOLOGY 0x0403 +#define USB_DEVICE_ID_RETRODE2 0x97c1 + +#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f +#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 + +#define USB_VENDOR_ID_ETT 0x0664 +#define USB_DEVICE_ID_TC5UH 0x0309 +#define USB_DEVICE_ID_TC4UM 0x0306 + +#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9 +#define USB_DEVICE_ID_ETURBOTOUCH 0x0006 +#define USB_DEVICE_ID_ETURBOTOUCH_2968 0x2968 + +#define USB_VENDOR_ID_EZKEY 0x0518 +#define USB_DEVICE_ID_BTC_8193 0x0002 + +#define USB_VENDOR_ID_FORMOSA 0x147a +#define USB_DEVICE_ID_FORMOSA_IR_RECEIVER 0xe03e + +#define USB_VENDOR_ID_FREESCALE 0x15A2 +#define USB_DEVICE_ID_FREESCALE_MX28 0x004F + +#define USB_VENDOR_ID_FRUCTEL 0x25B6 +#define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002 + +#define USB_VENDOR_ID_GAMERON 0x0810 +#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 +#define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002 + +#define USB_VENDOR_ID_GEMBIRD 0x11ff +#define USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2 0x3331 + +#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101 0x0101 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102 0x0102 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106 0x0106 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100 + +#define I2C_VENDOR_ID_GOODIX 0x27c6 +#define I2C_DEVICE_ID_GOODIX_01F0 0x01f0 + +#define USB_VENDOR_ID_GOODTOUCH 0x1aad +#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f + +#define USB_VENDOR_ID_GOOGLE 0x18d1 +#define USB_DEVICE_ID_GOOGLE_HAMMER 0x5022 +#define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028 +#define USB_DEVICE_ID_GOOGLE_STAFF 0x502b +#define USB_DEVICE_ID_GOOGLE_WAND 0x502d +#define USB_DEVICE_ID_GOOGLE_WHISKERS 0x5030 +#define USB_DEVICE_ID_GOOGLE_MASTERBALL 0x503c +#define USB_DEVICE_ID_GOOGLE_MAGNEMITE 0x503d + +#define USB_VENDOR_ID_GOTOP 0x08f2 +#define USB_DEVICE_ID_SUPER_Q2 0x007f +#define USB_DEVICE_ID_GOGOPEN 0x00ce +#define USB_DEVICE_ID_PENPOWER 0x00f4 + +#define USB_VENDOR_ID_GREENASIA 0x0e8f +#define USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD 0x3013 + +#define USB_VENDOR_ID_GRETAGMACBETH 0x0971 +#define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005 + +#define USB_VENDOR_ID_GRIFFIN 0x077d +#define USB_DEVICE_ID_POWERMATE 0x0410 +#define USB_DEVICE_ID_SOUNDKNOB 0x04AA +#define USB_DEVICE_ID_RADIOSHARK 0x627a + +#define USB_VENDOR_ID_GTCO 0x078c +#define USB_DEVICE_ID_GTCO_90 0x0090 +#define USB_DEVICE_ID_GTCO_100 0x0100 +#define USB_DEVICE_ID_GTCO_101 0x0101 +#define USB_DEVICE_ID_GTCO_103 0x0103 +#define USB_DEVICE_ID_GTCO_104 0x0104 +#define USB_DEVICE_ID_GTCO_105 0x0105 +#define USB_DEVICE_ID_GTCO_106 0x0106 +#define USB_DEVICE_ID_GTCO_107 0x0107 +#define USB_DEVICE_ID_GTCO_108 0x0108 +#define USB_DEVICE_ID_GTCO_200 0x0200 +#define USB_DEVICE_ID_GTCO_201 0x0201 +#define USB_DEVICE_ID_GTCO_202 0x0202 +#define USB_DEVICE_ID_GTCO_203 0x0203 +#define USB_DEVICE_ID_GTCO_204 0x0204 +#define USB_DEVICE_ID_GTCO_205 0x0205 +#define USB_DEVICE_ID_GTCO_206 0x0206 +#define USB_DEVICE_ID_GTCO_207 0x0207 +#define USB_DEVICE_ID_GTCO_300 0x0300 +#define USB_DEVICE_ID_GTCO_301 0x0301 +#define USB_DEVICE_ID_GTCO_302 0x0302 +#define USB_DEVICE_ID_GTCO_303 0x0303 +#define USB_DEVICE_ID_GTCO_304 0x0304 +#define USB_DEVICE_ID_GTCO_305 0x0305 +#define USB_DEVICE_ID_GTCO_306 0x0306 +#define USB_DEVICE_ID_GTCO_307 0x0307 +#define USB_DEVICE_ID_GTCO_308 0x0308 +#define USB_DEVICE_ID_GTCO_309 0x0309 +#define USB_DEVICE_ID_GTCO_400 0x0400 +#define USB_DEVICE_ID_GTCO_401 0x0401 +#define USB_DEVICE_ID_GTCO_402 0x0402 +#define USB_DEVICE_ID_GTCO_403 0x0403 +#define USB_DEVICE_ID_GTCO_404 0x0404 +#define USB_DEVICE_ID_GTCO_405 0x0405 +#define USB_DEVICE_ID_GTCO_500 0x0500 +#define USB_DEVICE_ID_GTCO_501 0x0501 +#define USB_DEVICE_ID_GTCO_502 0x0502 +#define USB_DEVICE_ID_GTCO_503 0x0503 +#define USB_DEVICE_ID_GTCO_504 0x0504 +#define USB_DEVICE_ID_GTCO_1000 0x1000 +#define USB_DEVICE_ID_GTCO_1001 0x1001 +#define USB_DEVICE_ID_GTCO_1002 0x1002 +#define USB_DEVICE_ID_GTCO_1003 0x1003 +#define USB_DEVICE_ID_GTCO_1004 0x1004 +#define USB_DEVICE_ID_GTCO_1005 0x1005 +#define USB_DEVICE_ID_GTCO_1006 0x1006 +#define USB_DEVICE_ID_GTCO_1007 0x1007 + +#define USB_VENDOR_ID_GYRATION 0x0c16 +#define USB_DEVICE_ID_GYRATION_REMOTE 0x0002 +#define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003 +#define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008 + +#define I2C_VENDOR_ID_HANTICK 0x0911 +#define I2C_PRODUCT_ID_HANTICK_5288 0x5288 + +#define USB_VENDOR_ID_HANWANG 0x0b57 +#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000 +#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff + +#define USB_VENDOR_ID_HANVON 0x20b3 +#define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18 + +#define USB_VENDOR_ID_HANVON_ALT 0x22ed +#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH 0x1010 + +#define USB_VENDOR_ID_HAPP 0x078b +#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 +#define USB_DEVICE_ID_UGCI_FLYING 0x0020 +#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 + +#define USB_VENDOR_ID_HP 0x03f0 +#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a +#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A 0x094a +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0941 0x0941 +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641 0x0641 + +#define USB_VENDOR_ID_HUION 0x256c +#define USB_DEVICE_ID_HUION_TABLET 0x006e +#define USB_DEVICE_ID_HUION_HS64 0x006d + +#define USB_VENDOR_ID_IBM 0x04b3 +#define USB_DEVICE_ID_IBM_SCROLLPOINT_III 0x3100 +#define USB_DEVICE_ID_IBM_SCROLLPOINT_PRO 0x3103 +#define USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL 0x3105 +#define USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL 0x3108 +#define USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO 0x3109 + +#define USB_VENDOR_ID_IDEACOM 0x1cb6 +#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 +#define USB_DEVICE_ID_IDEACOM_IDC6651 0x6651 +#define USB_DEVICE_ID_IDEACOM_IDC6680 0x6680 + +#define USB_VENDOR_ID_ILITEK 0x222a +#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001 + +#define USB_VENDOR_ID_INTEL_0 0x8086 +#define USB_VENDOR_ID_INTEL_1 0x8087 +#define USB_DEVICE_ID_INTEL_HID_SENSOR_0 0x09fa +#define USB_DEVICE_ID_INTEL_HID_SENSOR_1 0x0a04 + +#define USB_VENDOR_ID_STM_0 0x0483 +#define USB_DEVICE_ID_STM_HID_SENSOR 0x91d1 +#define USB_DEVICE_ID_STM_HID_SENSOR_1 0x9100 + +#define USB_VENDOR_ID_ION 0x15e4 +#define USB_DEVICE_ID_ICADE 0x0132 + +#define USB_VENDOR_ID_HOLTEK 0x1241 +#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015 + +#define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 +#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A 0xa04a +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067 0xa067 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2 0xa0c2 +#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096 + +#define USB_VENDOR_ID_IMATION 0x0718 +#define USB_DEVICE_ID_DISC_STAKKA 0xd000 + +#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 +#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 + +#define USB_VENDOR_ID_INNOMEDIA 0x1292 +#define USB_DEVICE_ID_INNEX_GENESIS_ATARI 0x4745 + +#define USB_VENDOR_ID_ITE 0x048d +#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 +#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 +#define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 +#define USB_DEVICE_ID_ITE8595 0x8595 + +#define USB_VENDOR_ID_JABRA 0x0b0e +#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 +#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 +#define USB_DEVICE_ID_JABRA_GN9350E 0x9350 + +#define USB_VENDOR_ID_JESS 0x0c45 +#define USB_DEVICE_ID_JESS_YUREX 0x1010 +#define USB_DEVICE_ID_ASUS_MD_5112 0x5112 +#define USB_DEVICE_ID_REDRAGON_ASURA 0x760b + +#define USB_VENDOR_ID_JESS2 0x0f30 +#define USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD 0x0111 + +#define USB_VENDOR_ID_KBGEAR 0x084e +#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 + +#define USB_VENDOR_ID_KENSINGTON 0x047d +#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041 + +#define USB_VENDOR_ID_KWORLD 0x1b80 +#define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700 + +#define USB_VENDOR_ID_KEYTOUCH 0x0926 +#define USB_DEVICE_ID_KEYTOUCH_IEC 0x3333 + +#define USB_VENDOR_ID_KYE 0x0458 +#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 +#define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138 +#define USB_DEVICE_ID_GENIUS_MANTICORE 0x0153 +#define USB_DEVICE_ID_GENIUS_GX_IMPERATOR 0x4018 +#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 +#define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 +#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 +#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a +#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 +#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 +#define USB_DEVICE_ID_KYE_EASYPEN_M406XE 0x5019 + +#define USB_VENDOR_ID_LABTEC 0x1020 +#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 + +#define USB_VENDOR_ID_LCPOWER 0x1241 +#define USB_DEVICE_ID_LCPOWER_LC1000 0xf767 + +#define USB_VENDOR_ID_LD 0x0f11 +#define USB_DEVICE_ID_LD_CASSY 0x1000 +#define USB_DEVICE_ID_LD_CASSY2 0x1001 +#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 +#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011 +#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 +#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021 +#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031 +#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032 +#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 +#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 +#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 +#define USB_DEVICE_ID_LD_POWERANALYSERCASSY 0x1040 +#define USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY 0x1042 +#define USB_DEVICE_ID_LD_MACHINETESTCASSY 0x1043 +#define USB_DEVICE_ID_LD_JWM 0x1080 +#define USB_DEVICE_ID_LD_DMMP 0x1081 +#define USB_DEVICE_ID_LD_UMIP 0x1090 +#define USB_DEVICE_ID_LD_UMIC 0x10A0 +#define USB_DEVICE_ID_LD_UMIB 0x10B0 +#define USB_DEVICE_ID_LD_XRAY 0x1100 +#define USB_DEVICE_ID_LD_XRAY2 0x1101 +#define USB_DEVICE_ID_LD_XRAYCT 0x1110 +#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 +#define USB_DEVICE_ID_LD_MOTOR 0x1210 +#define USB_DEVICE_ID_LD_COM3LAB 0x2000 +#define USB_DEVICE_ID_LD_TELEPORT 0x2010 +#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 +#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 +#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 +#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050 +#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051 +#define USB_DEVICE_ID_LD_ABSESP 0x2060 +#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070 +#define USB_DEVICE_ID_LD_MCT 0x2080 +#define USB_DEVICE_ID_LD_HYBRID 0x2090 +#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 + +#define USB_VENDOR_ID_LENOVO 0x17ef +#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 +#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047 +#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048 +#define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL 0x6049 +#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067 +#define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085 +#define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3 +#define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5 + +#define USB_VENDOR_ID_LG 0x1fd2 +#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 +#define USB_DEVICE_ID_LG_MELFAS_MT 0x6007 +#define I2C_DEVICE_ID_LG_8001 0x8001 + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e +#define USB_DEVICE_ID_LOGITECH_T651 0xb00c +#define USB_DEVICE_ID_LOGITECH_C007 0xc007 +#define USB_DEVICE_ID_LOGITECH_C077 0xc077 +#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 +#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 +#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f +#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306 +#define USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS 0xc24d +#define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a +#define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a +#define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 +#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 +#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216 +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 +#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f +#define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262 +#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 +#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 +#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 +#define USB_DEVICE_ID_LOGITECH_WINGMAN_FG 0xc20e +#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 +#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 +#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 +#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 +#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 +#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a +#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b +#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c +#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a +#define USB_DEVICE_ID_S510_RECEIVER 0xc50c +#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 +#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER 0xc51b +#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER 0xc52f +#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2 0xc534 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a +#define USB_DEVICE_ID_SPACETRAVELLER 0xc623 +#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 +#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 +#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 +#define USB_DEVICE_ID_DINOVO_MINI 0xc71f +#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03 +#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL 0xca04 + +#define USB_VENDOR_ID_LUMIO 0x202e +#define USB_DEVICE_ID_CRYSTALTOUCH 0x0006 +#define USB_DEVICE_ID_CRYSTALTOUCH_DUAL 0x0007 + +#define USB_VENDOR_ID_MADCATZ 0x0738 +#define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_DEVICE_ID_MADCATZ_RAT5 0x1705 +#define USB_DEVICE_ID_MADCATZ_RAT9 0x1709 + +#define USB_VENDOR_ID_MCC 0x09db +#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 +#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a + +#define USB_VENDOR_ID_MCS 0x16d0 +#define USB_DEVICE_ID_MCS_GAMEPADBLOCK 0x0bcc + +#define USB_VENDOR_ID_MGE 0x0463 +#define USB_DEVICE_ID_MGE_UPS 0xffff +#define USB_DEVICE_ID_MGE_UPS1 0x0001 + +#define USB_VENDOR_ID_MICROCHIP 0x04d8 +#define USB_DEVICE_ID_PICKIT1 0x0032 +#define USB_DEVICE_ID_PICKIT2 0x0033 +#define USB_DEVICE_ID_PICOLCD 0xc002 +#define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 +#define USB_DEVICE_ID_PICK16F1454 0x0042 +#define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7 +#define USB_DEVICE_ID_LUXAFOR 0xf372 + +#define USB_VENDOR_ID_MICROSOFT 0x045e +#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b +#define USB_DEVICE_ID_MS_OFFICE_KB 0x0048 +#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d +#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K 0x00b4 +#define USB_DEVICE_ID_MS_NE4K 0x00db +#define USB_DEVICE_ID_MS_NE4K_JP 0x00dc +#define USB_DEVICE_ID_MS_LK6K 0x00f9 +#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 +#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 +#define USB_DEVICE_ID_MS_NE7K 0x071d +#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730 +#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732 +#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600 0x0750 +#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c +#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 +#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 +#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 +#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 +#define USB_DEVICE_ID_MS_POWER_COVER 0x07da +#define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd +#define USB_DEVICE_ID_MS_PIXART_MOUSE 0x00cb + +#define USB_VENDOR_ID_MOJO 0x8282 +#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 + +#define USB_VENDOR_ID_MONTEREY 0x0566 +#define USB_DEVICE_ID_GENIUS_KB29E 0x3004 + +#define USB_VENDOR_ID_MSI 0x1770 +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00 + +#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 +#define USB_DEVICE_ID_N_S_HARMONY 0xc359 + +#define USB_VENDOR_ID_NATSU 0x08b7 +#define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001 + +#define USB_VENDOR_ID_NCR 0x0404 +#define USB_DEVICE_ID_NCR_FIRST 0x0300 +#define USB_DEVICE_ID_NCR_LAST 0x03ff + +#define USB_VENDOR_ID_NEC 0x073e +#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 + +#define USB_VENDOR_ID_NEXIO 0x1870 +#define USB_DEVICE_ID_NEXIO_MULTITOUCH_420 0x010d +#define USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750 0x0110 + +#define USB_VENDOR_ID_NEXTWINDOW 0x1926 +#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003 + +#define USB_VENDOR_ID_NINTENDO 0x057e +#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 +#define USB_DEVICE_ID_NINTENDO_WIIMOTE2 0x0330 +#define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006 +#define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007 +#define USB_DEVICE_ID_NINTENDO_PROCON 0x2009 +#define USB_DEVICE_ID_NINTENDO_CHRGGRIP 0x200E +#define USB_DEVICE_ID_NINTENDO_SNESCON 0x2017 + +#define USB_VENDOR_ID_NOVATEK 0x0603 +#define USB_DEVICE_ID_NOVATEK_PCT 0x0600 +#define USB_DEVICE_ID_NOVATEK_MOUSE 0x1602 + +#define USB_VENDOR_ID_NTI 0x0757 +#define USB_DEVICE_ID_USB_SUN 0x0a00 + +#define USB_VENDOR_ID_NTRIG 0x1b96 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2 0x0004 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_3 0x0005 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_4 0x0006 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_5 0x0007 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_6 0x0008 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_7 0x0009 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_8 0x000A +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_9 0x000B +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_10 0x000C +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_11 0x000D +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_12 0x000E +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_13 0x000F +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_14 0x0010 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_15 0x0011 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014 +#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500 + +#define USB_VENDOR_ID_ONTRAK 0x0a07 +#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 + +#define USB_VENDOR_ID_ORTEK 0x05a4 +#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 +#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 +#define USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S 0x8003 + +#define USB_VENDOR_ID_PLANTRONICS 0x047f + +#define USB_VENDOR_ID_PANASONIC 0x04da +#define USB_DEVICE_ID_PANABOARD_UBT780 0x1044 +#define USB_DEVICE_ID_PANABOARD_UBT880 0x104d + +#define USB_VENDOR_ID_PANJIT 0x134c + +#define USB_VENDOR_ID_PANTHERLORD 0x0810 +#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 + +#define USB_VENDOR_ID_PENMOUNT 0x14e1 +#define USB_DEVICE_ID_PENMOUNT_PCI 0x3500 +#define USB_DEVICE_ID_PENMOUNT_1610 0x1610 +#define USB_DEVICE_ID_PENMOUNT_1640 0x1640 +#define USB_DEVICE_ID_PENMOUNT_6000 0x6000 + +#define USB_VENDOR_ID_PETALYNX 0x18b1 +#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 + +#define USB_VENDOR_ID_PETZL 0x2122 +#define USB_DEVICE_ID_PETZL_HEADLAMP 0x1234 + +#define USB_VENDOR_ID_PHILIPS 0x0471 +#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 + +#define USB_VENDOR_ID_PI_ENGINEERING 0x05f3 +#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff + +#define USB_VENDOR_ID_PIXART 0x093a +#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2 0x0137 +#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE 0x2510 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003 + +#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 +#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 + +#define USB_VENDOR_ID_POWERCOM 0x0d9f +#define USB_DEVICE_ID_POWERCOM_UPS 0x0002 + +#define USB_VENDOR_ID_PRODIGE 0x05af +#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 + +#define USB_VENDOR_ID_QUANTA 0x0408 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003 0x3003 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 + +#define I2C_VENDOR_ID_RAYDIUM 0x2386 +#define I2C_PRODUCT_ID_RAYDIUM_4B33 0x4b33 + +#define USB_VENDOR_ID_RAZER 0x1532 +#define USB_DEVICE_ID_RAZER_BLADE_14 0x011D + +#define USB_VENDOR_ID_REALTEK 0x0bda +#define USB_DEVICE_ID_REALTEK_READER 0x0152 + +#define USB_VENDOR_ID_RETROUSB 0xf000 +#define USB_DEVICE_ID_RETROUSB_SNES_RETROPAD 0x0003 +#define USB_DEVICE_ID_RETROUSB_SNES_RETROPORT 0x00f1 + +#define USB_VENDOR_ID_ROCCAT 0x1e7d +#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 +#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c +#define USB_DEVICE_ID_ROCCAT_ISKUFX 0x3264 +#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced +#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe +#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4 +#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 +#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 +#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 +#define USB_DEVICE_ID_ROCCAT_RYOS_MK 0x3138 +#define USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW 0x31ce +#define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO 0x3232 +#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a + +#define USB_VENDOR_ID_SAITEK 0x06a3 +#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 +#define USB_DEVICE_ID_SAITEK_PS1000 0x0621 +#define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb +#define USB_DEVICE_ID_SAITEK_RAT7_CONTAGION 0x0ccd +#define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 +#define USB_DEVICE_ID_SAITEK_RAT9 0x0cfa +#define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 +#define USB_DEVICE_ID_SAITEK_X52 0x075c + +#define USB_VENDOR_ID_SAMSUNG 0x0419 +#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 +#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600 + +#define USB_VENDOR_ID_SEMICO 0x1a2c +#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023 +#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2 0x0027 + +#define USB_VENDOR_ID_SENNHEISER 0x1395 +#define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c + +#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f +#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 + +#define USB_VENDOR_ID_SIGMATEL 0x066F +#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780 + +#define USB_VENDOR_ID_SIS_TOUCH 0x0457 +#define USB_DEVICE_ID_SIS9200_TOUCH 0x9200 +#define USB_DEVICE_ID_SIS817_TOUCH 0x0817 +#define USB_DEVICE_ID_SIS_TS 0x1013 +#define USB_DEVICE_ID_SIS1030_TOUCH 0x1030 + +#define USB_VENDOR_ID_SKYCABLE 0x1223 +#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 + +#define USB_VENDOR_ID_SMK 0x0609 +#define USB_DEVICE_ID_SMK_PS3_BDREMOTE 0x0306 +#define USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE 0x0368 +#define USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE 0x0369 + + +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b +#define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374 +#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE 0x0ba0 +#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5 +#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f +#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 +#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 + +#define USB_VENDOR_ID_SINO_LITE 0x1345 +#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008 + +#define USB_VENDOR_ID_SOLID_YEAR 0x060b +#define USB_DEVICE_ID_MACALLY_IKEY_KEYBOARD 0x0001 +#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a +#define USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD 0x700a + +#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 + +#define USB_VENDOR_ID_STANTUM 0x1f87 +#define USB_DEVICE_ID_MTP 0x0002 + +#define USB_VENDOR_ID_STANTUM_STM 0x0483 +#define USB_DEVICE_ID_MTP_STM 0x3261 + +#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403 +#define USB_DEVICE_ID_MTP_SITRONIX 0x5001 + +#define USB_VENDOR_ID_VALVE 0x28de +#define USB_DEVICE_ID_STEAM_CONTROLLER 0x1102 +#define USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS 0x1142 + +#define USB_VENDOR_ID_STEELSERIES 0x1038 +#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 + +#define USB_VENDOR_ID_SUN 0x0430 +#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab + +#define USB_VENDOR_ID_SUNPLUS 0x04fc +#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 + +#define USB_VENDOR_ID_SYMBOL 0x05e0 +#define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800 +#define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300 +#define USB_DEVICE_ID_SYMBOL_SCANNER_3 0x1200 + +#define USB_VENDOR_ID_SYNAPTICS 0x06cb +#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 +#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 +#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 +#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 +#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 +#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 +#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 +#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 +#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 +#define USB_DEVICE_ID_SYNAPTICS_LTS1 0x0af8 +#define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 +#define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 +#define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 +#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 +#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7 + +#define USB_VENDOR_ID_TEXAS_INSTRUMENTS 0x2047 +#define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA 0x0855 + +#define USB_VENDOR_ID_THINGM 0x27b8 +#define USB_DEVICE_ID_BLINK1 0x01ed + +#define USB_VENDOR_ID_THQ 0x20d6 +#define USB_DEVICE_ID_THQ_PS3_UDRAW 0xcb17 + +#define USB_VENDOR_ID_THRUSTMASTER 0x044f + +#define USB_VENDOR_ID_TIVO 0x150a +#define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200 +#define USB_DEVICE_ID_TIVO_SLIDE 0x1201 +#define USB_DEVICE_ID_TIVO_SLIDE_PRO 0x1203 + +#define USB_VENDOR_ID_TOPSEED 0x0766 +#define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 + +#define USB_VENDOR_ID_TOPSEED2 0x1784 +#define USB_DEVICE_ID_TOPSEED2_RF_COMBO 0x0004 +#define USB_DEVICE_ID_TOPSEED2_PERIPAD_701 0x0016 + +#define USB_VENDOR_ID_TOPMAX 0x0663 +#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 + +#define USB_VENDOR_ID_TOUCH_INTL 0x1e5e +#define USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH 0x0313 + +#define USB_VENDOR_ID_TOUCHPACK 0x1bfd +#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688 + +#define USB_VENDOR_ID_TPV 0x25aa +#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882 0x8882 +#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8883 0x8883 + +#define USB_VENDOR_ID_TURBOX 0x062a +#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_DEVICE_ID_ASUS_MD_5110 0x5110 +#define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100 + +#define USB_VENDOR_ID_TWINHAN 0x6253 +#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100 + +#define USB_VENDOR_ID_UCLOGIC 0x5543 +#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 +#define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5 0x6001 +#define USB_DEVICE_ID_UCLOGIC_TABLET_TWA60 0x0064 +#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003 +#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004 +#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 +#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 +#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 +#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 +#define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031 +#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81 0x0081 +#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45 0x0045 +#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47 0x0047 +#define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d + +#define USB_VENDOR_ID_UGEE 0x28bd +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 +#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 +#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 +#define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055 + +#define USB_VENDOR_ID_UNITEC 0x227d +#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 +#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19 0x0a19 + +#define USB_VENDOR_ID_VELLEMAN 0x10cf +#define USB_DEVICE_ID_VELLEMAN_K8055_FIRST 0x5500 +#define USB_DEVICE_ID_VELLEMAN_K8055_LAST 0x5503 +#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061 +#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068 + +#define USB_VENDOR_ID_VTL 0x0306 +#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 +#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD + +#define USB_VENDOR_ID_WALTOP 0x172f +#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032 +#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH 0x0034 +#define USB_DEVICE_ID_WALTOP_Q_PAD 0x0037 +#define USB_DEVICE_ID_WALTOP_PID_0038 0x0038 +#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501 +#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500 +#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502 + +#define USB_VENDOR_ID_WEIDA 0x2575 +#define USB_DEVICE_ID_WEIDA_8752 0xC300 +#define USB_DEVICE_ID_WEIDA_8755 0xC301 + +#define USB_VENDOR_ID_WISEGROUP 0x0925 +#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 +#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888 +#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800 +#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 + +#define USB_VENDOR_ID_WISEGROUP_LTD 0x6666 +#define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677 +#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 +#define USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO 0x8801 +#define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802 +#define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804 + +#define USB_VENDOR_ID_WISTRON 0x0fb8 +#define USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH 0x1109 + +#define USB_VENDOR_ID_X_TENSIONS 0x1ae7 +#define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001 + +#define USB_VENDOR_ID_XAT 0x2505 +#define USB_DEVICE_ID_XAT_CSR 0x0220 + +#define USB_VENDOR_ID_XIN_MO 0x16c0 +#define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1 +#define USB_DEVICE_ID_THT_2P_ARCADE 0x75e1 + +#define USB_VENDOR_ID_XIROKU 0x1477 +#define USB_DEVICE_ID_XIROKU_SPX 0x1006 +#define USB_DEVICE_ID_XIROKU_MPX 0x1007 +#define USB_DEVICE_ID_XIROKU_CSR 0x100e +#define USB_DEVICE_ID_XIROKU_SPX1 0x1021 +#define USB_DEVICE_ID_XIROKU_CSR1 0x1022 +#define USB_DEVICE_ID_XIROKU_MPX1 0x1023 +#define USB_DEVICE_ID_XIROKU_SPX2 0x1024 +#define USB_DEVICE_ID_XIROKU_CSR2 0x1025 +#define USB_DEVICE_ID_XIROKU_MPX2 0x1026 + +#define USB_VENDOR_ID_YEALINK 0x6993 +#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 + +#define USB_VENDOR_ID_ZEROPLUS 0x0c12 + +#define USB_VENDOR_ID_ZYDACRON 0x13EC +#define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006 + +#define USB_VENDOR_ID_ZYTRONIC 0x14c8 +#define USB_DEVICE_ID_ZYTRONIC_ZXY100 0x0005 + +#define USB_VENDOR_ID_PRIMAX 0x0461 +#define USB_DEVICE_ID_PRIMAX_MOUSE_4D22 0x4d22 +#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 +#define USB_DEVICE_ID_PRIMAX_REZEL 0x4e72 +#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D0F 0x4d0f +#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D65 0x4d65 +#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4E22 0x4e22 + + +#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */ +#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */ + +#define USB_VENDOR_ID_MULTIPLE_1781 0x1781 +#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a9d + +#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b +#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002 +#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003 + +#define USB_VENDOR_ID_UGTIZER 0x2179 +#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053 + +#define USB_VENDOR_ID_VIEWSONIC 0x0543 +#define USB_DEVICE_ID_VIEWSONIC_PD1011 0xe621 + +#define USB_VENDOR_ID_SIGNOTEC 0x2133 +#define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018 + +#endif diff --git a/hid-nintendo.c b/hid-nintendo.c new file mode 100644 index 000000000000..71d564e74b7e --- /dev/null +++ b/hid-nintendo.c @@ -0,0 +1,1971 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for Nintendo Switch Joy-Cons and Pro Controllers + * + * Copyright (c) 2019 Daniel J. Ogorchock <djogorchock@gmail.com> + * + * The following resources/projects were referenced for this driver: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + * https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin) + * https://github.com/FrotBot/SwitchProConLinuxUSB + * https://github.com/MTCKC/ProconXInput + * https://github.com/Davidobot/BetterJoyForCemu + * hid-wiimote kernel hid driver + * hid-logitech-hidpp driver + * hid-sony driver + * + * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The + * Pro Controllers can either be used over USB or Bluetooth. + * + * The driver will retrieve the factory calibration info from the controllers, + * so little to no user calibration should be required. + * + */ + +#include "hid-ids.h" +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/jiffies.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/spinlock.h> + +/* + * Reference the url below for the following HID report defines: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + */ + +/* Output Reports */ +static const u8 JC_OUTPUT_RUMBLE_AND_SUBCMD = 0x01; +static const u8 JC_OUTPUT_FW_UPDATE_PKT = 0x03; +static const u8 JC_OUTPUT_RUMBLE_ONLY = 0x10; +static const u8 JC_OUTPUT_MCU_DATA = 0x11; +static const u8 JC_OUTPUT_USB_CMD = 0x80; + +/* Subcommand IDs */ +static const u8 JC_SUBCMD_STATE /*= 0x00*/; +static const u8 JC_SUBCMD_MANUAL_BT_PAIRING = 0x01; +static const u8 JC_SUBCMD_REQ_DEV_INFO = 0x02; +static const u8 JC_SUBCMD_SET_REPORT_MODE = 0x03; +static const u8 JC_SUBCMD_TRIGGERS_ELAPSED = 0x04; +static const u8 JC_SUBCMD_GET_PAGE_LIST_STATE = 0x05; +static const u8 JC_SUBCMD_SET_HCI_STATE = 0x06; +static const u8 JC_SUBCMD_RESET_PAIRING_INFO = 0x07; +static const u8 JC_SUBCMD_LOW_POWER_MODE = 0x08; +static const u8 JC_SUBCMD_SPI_FLASH_READ = 0x10; +static const u8 JC_SUBCMD_SPI_FLASH_WRITE = 0x11; +static const u8 JC_SUBCMD_RESET_MCU = 0x20; +static const u8 JC_SUBCMD_SET_MCU_CONFIG = 0x21; +static const u8 JC_SUBCMD_SET_MCU_STATE = 0x22; +static const u8 JC_SUBCMD_SET_PLAYER_LIGHTS = 0x30; +static const u8 JC_SUBCMD_GET_PLAYER_LIGHTS = 0x31; +static const u8 JC_SUBCMD_SET_HOME_LIGHT = 0x38; +static const u8 JC_SUBCMD_ENABLE_IMU = 0x40; +static const u8 JC_SUBCMD_SET_IMU_SENSITIVITY = 0x41; +static const u8 JC_SUBCMD_WRITE_IMU_REG = 0x42; +static const u8 JC_SUBCMD_READ_IMU_REG = 0x43; +static const u8 JC_SUBCMD_ENABLE_VIBRATION = 0x48; +static const u8 JC_SUBCMD_GET_REGULATED_VOLTAGE = 0x50; + +/* Input Reports */ +static const u8 JC_INPUT_BUTTON_EVENT = 0x3F; +static const u8 JC_INPUT_SUBCMD_REPLY = 0x21; +static const u8 JC_INPUT_IMU_DATA = 0x30; +static const u8 JC_INPUT_MCU_DATA = 0x31; +static const u8 JC_INPUT_USB_RESPONSE = 0x81; + +/* Feature Reports */ +static const u8 JC_FEATURE_LAST_SUBCMD = 0x02; +static const u8 JC_FEATURE_OTA_FW_UPGRADE = 0x70; +static const u8 JC_FEATURE_SETUP_MEM_READ = 0x71; +static const u8 JC_FEATURE_MEM_READ = 0x72; +static const u8 JC_FEATURE_ERASE_MEM_SECTOR = 0x73; +static const u8 JC_FEATURE_MEM_WRITE = 0x74; +static const u8 JC_FEATURE_LAUNCH = 0x75; + +/* USB Commands */ +static const u8 JC_USB_CMD_CONN_STATUS = 0x01; +static const u8 JC_USB_CMD_HANDSHAKE = 0x02; +static const u8 JC_USB_CMD_BAUDRATE_3M = 0x03; +static const u8 JC_USB_CMD_NO_TIMEOUT = 0x04; +static const u8 JC_USB_CMD_EN_TIMEOUT = 0x05; +static const u8 JC_USB_RESET = 0x06; +static const u8 JC_USB_PRE_HANDSHAKE = 0x91; +static const u8 JC_USB_SEND_UART = 0x92; + +/* SPI storage addresses of factory calibration data */ +static const u16 JC_CAL_DATA_START = 0x603d; +static const u16 JC_CAL_DATA_END = 0x604e; +#define JC_CAL_DATA_SIZE (JC_CAL_DATA_END - JC_CAL_DATA_START + 1) + +/* SPI storage addresses of IMU factory calibration data */ +static const u16 JC_IMU_CAL_DATA_START = 0x6020; +static const u16 JC_IMU_CAL_DATA_END = 0x6037; +#define JC_IMU_CAL_DATA_SIZE \ + (JC_IMU_CAL_DATA_END - JC_IMU_CAL_DATA_START + 1) + +/* The raw analog joystick values will be mapped in terms of this magnitude */ +static const u16 JC_MAX_STICK_MAG = 32767; +static const u16 JC_STICK_FUZZ = 250; +static const u16 JC_STICK_FLAT = 500; + +/* The accel axes will be mapped in terms of this magnitude */ +static const u16 JC_MAX_ACCEL_MAG = 32767; +static const u16 JC_ACCEL_RES = 4096; +static const u16 JC_ACCEL_FUZZ = 10; +static const u16 JC_ACCEL_FLAT /*= 0*/; + +/* The gyro axes will be mapped in terms of this magnitude */ +static const u16 JC_MAX_GYRO_MAG = 32767; +static const u16 JC_GYRO_RES = 13371 / 936; /* 14 (14.285) */ +static const u16 JC_GYRO_FUZZ = 10; +static const u16 JC_GYRO_FLAT /*= 0*/; + +/* frequency/amplitude tables for rumble */ +struct joycon_rumble_freq_data { + u16 high; + u8 low; + u16 freq; /* Hz*/ +}; + +struct joycon_rumble_amp_data { + u8 high; + u16 low; + u16 amp; +}; + +/* + * These tables are from + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md + */ +static const struct joycon_rumble_freq_data joycon_rumble_frequencies[] = { + /* high, low, freq */ + { 0x0000, 0x01, 41 }, { 0x0000, 0x02, 42 }, { 0x0000, 0x03, 43 }, + { 0x0000, 0x04, 44 }, { 0x0000, 0x05, 45 }, { 0x0000, 0x06, 46 }, + { 0x0000, 0x07, 47 }, { 0x0000, 0x08, 48 }, { 0x0000, 0x09, 49 }, + { 0x0000, 0x0A, 50 }, { 0x0000, 0x0B, 51 }, { 0x0000, 0x0C, 52 }, + { 0x0000, 0x0D, 53 }, { 0x0000, 0x0E, 54 }, { 0x0000, 0x0F, 55 }, + { 0x0000, 0x10, 57 }, { 0x0000, 0x11, 58 }, { 0x0000, 0x12, 59 }, + { 0x0000, 0x13, 60 }, { 0x0000, 0x14, 62 }, { 0x0000, 0x15, 63 }, + { 0x0000, 0x16, 64 }, { 0x0000, 0x17, 66 }, { 0x0000, 0x18, 67 }, + { 0x0000, 0x19, 69 }, { 0x0000, 0x1A, 70 }, { 0x0000, 0x1B, 72 }, + { 0x0000, 0x1C, 73 }, { 0x0000, 0x1D, 75 }, { 0x0000, 0x1e, 77 }, + { 0x0000, 0x1f, 78 }, { 0x0000, 0x20, 80 }, { 0x0400, 0x21, 82 }, + { 0x0800, 0x22, 84 }, { 0x0c00, 0x23, 85 }, { 0x1000, 0x24, 87 }, + { 0x1400, 0x25, 89 }, { 0x1800, 0x26, 91 }, { 0x1c00, 0x27, 93 }, + { 0x2000, 0x28, 95 }, { 0x2400, 0x29, 97 }, { 0x2800, 0x2a, 99 }, + { 0x2c00, 0x2b, 102 }, { 0x3000, 0x2c, 104 }, { 0x3400, 0x2d, 106 }, + { 0x3800, 0x2e, 108 }, { 0x3c00, 0x2f, 111 }, { 0x4000, 0x30, 113 }, + { 0x4400, 0x31, 116 }, { 0x4800, 0x32, 118 }, { 0x4c00, 0x33, 121 }, + { 0x5000, 0x34, 123 }, { 0x5400, 0x35, 126 }, { 0x5800, 0x36, 129 }, + { 0x5c00, 0x37, 132 }, { 0x6000, 0x38, 135 }, { 0x6400, 0x39, 137 }, + { 0x6800, 0x3a, 141 }, { 0x6c00, 0x3b, 144 }, { 0x7000, 0x3c, 147 }, + { 0x7400, 0x3d, 150 }, { 0x7800, 0x3e, 153 }, { 0x7c00, 0x3f, 157 }, + { 0x8000, 0x40, 160 }, { 0x8400, 0x41, 164 }, { 0x8800, 0x42, 167 }, + { 0x8c00, 0x43, 171 }, { 0x9000, 0x44, 174 }, { 0x9400, 0x45, 178 }, + { 0x9800, 0x46, 182 }, { 0x9c00, 0x47, 186 }, { 0xa000, 0x48, 190 }, + { 0xa400, 0x49, 194 }, { 0xa800, 0x4a, 199 }, { 0xac00, 0x4b, 203 }, + { 0xb000, 0x4c, 207 }, { 0xb400, 0x4d, 212 }, { 0xb800, 0x4e, 217 }, + { 0xbc00, 0x4f, 221 }, { 0xc000, 0x50, 226 }, { 0xc400, 0x51, 231 }, + { 0xc800, 0x52, 236 }, { 0xcc00, 0x53, 241 }, { 0xd000, 0x54, 247 }, + { 0xd400, 0x55, 252 }, { 0xd800, 0x56, 258 }, { 0xdc00, 0x57, 263 }, + { 0xe000, 0x58, 269 }, { 0xe400, 0x59, 275 }, { 0xe800, 0x5a, 281 }, + { 0xec00, 0x5b, 287 }, { 0xf000, 0x5c, 293 }, { 0xf400, 0x5d, 300 }, + { 0xf800, 0x5e, 306 }, { 0xfc00, 0x5f, 313 }, { 0x0001, 0x60, 320 }, + { 0x0401, 0x61, 327 }, { 0x0801, 0x62, 334 }, { 0x0c01, 0x63, 341 }, + { 0x1001, 0x64, 349 }, { 0x1401, 0x65, 357 }, { 0x1801, 0x66, 364 }, + { 0x1c01, 0x67, 372 }, { 0x2001, 0x68, 381 }, { 0x2401, 0x69, 389 }, + { 0x2801, 0x6a, 397 }, { 0x2c01, 0x6b, 406 }, { 0x3001, 0x6c, 415 }, + { 0x3401, 0x6d, 424 }, { 0x3801, 0x6e, 433 }, { 0x3c01, 0x6f, 443 }, + { 0x4001, 0x70, 453 }, { 0x4401, 0x71, 462 }, { 0x4801, 0x72, 473 }, + { 0x4c01, 0x73, 483 }, { 0x5001, 0x74, 494 }, { 0x5401, 0x75, 504 }, + { 0x5801, 0x76, 515 }, { 0x5c01, 0x77, 527 }, { 0x6001, 0x78, 538 }, + { 0x6401, 0x79, 550 }, { 0x6801, 0x7a, 562 }, { 0x6c01, 0x7b, 574 }, + { 0x7001, 0x7c, 587 }, { 0x7401, 0x7d, 600 }, { 0x7801, 0x7e, 613 }, + { 0x7c01, 0x7f, 626 }, { 0x8001, 0x00, 640 }, { 0x8401, 0x00, 654 }, + { 0x8801, 0x00, 668 }, { 0x8c01, 0x00, 683 }, { 0x9001, 0x00, 698 }, + { 0x9401, 0x00, 713 }, { 0x9801, 0x00, 729 }, { 0x9c01, 0x00, 745 }, + { 0xa001, 0x00, 761 }, { 0xa401, 0x00, 778 }, { 0xa801, 0x00, 795 }, + { 0xac01, 0x00, 812 }, { 0xb001, 0x00, 830 }, { 0xb401, 0x00, 848 }, + { 0xb801, 0x00, 867 }, { 0xbc01, 0x00, 886 }, { 0xc001, 0x00, 905 }, + { 0xc401, 0x00, 925 }, { 0xc801, 0x00, 945 }, { 0xcc01, 0x00, 966 }, + { 0xd001, 0x00, 987 }, { 0xd401, 0x00, 1009 }, { 0xd801, 0x00, 1031 }, + { 0xdc01, 0x00, 1053 }, { 0xe001, 0x00, 1076 }, { 0xe401, 0x00, 1100 }, + { 0xe801, 0x00, 1124 }, { 0xec01, 0x00, 1149 }, { 0xf001, 0x00, 1174 }, + { 0xf401, 0x00, 1199 }, { 0xf801, 0x00, 1226 }, { 0xfc01, 0x00, 1253 } +}; + +#define joycon_max_rumble_amp (1003) +static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] = { + /* high, low, amp */ + { 0x00, 0x0040, 0 }, + { 0x02, 0x8040, 10 }, { 0x04, 0x0041, 12 }, { 0x06, 0x8041, 14 }, + { 0x08, 0x0042, 17 }, { 0x0a, 0x8042, 20 }, { 0x0c, 0x0043, 24 }, + { 0x0e, 0x8043, 28 }, { 0x10, 0x0044, 33 }, { 0x12, 0x8044, 40 }, + { 0x14, 0x0045, 47 }, { 0x16, 0x8045, 56 }, { 0x18, 0x0046, 67 }, + { 0x1a, 0x8046, 80 }, { 0x1c, 0x0047, 95 }, { 0x1e, 0x8047, 112 }, + { 0x20, 0x0048, 117 }, { 0x22, 0x8048, 123 }, { 0x24, 0x0049, 128 }, + { 0x26, 0x8049, 134 }, { 0x28, 0x004a, 140 }, { 0x2a, 0x804a, 146 }, + { 0x2c, 0x004b, 152 }, { 0x2e, 0x804b, 159 }, { 0x30, 0x004c, 166 }, + { 0x32, 0x804c, 173 }, { 0x34, 0x004d, 181 }, { 0x36, 0x804d, 189 }, + { 0x38, 0x004e, 198 }, { 0x3a, 0x804e, 206 }, { 0x3c, 0x004f, 215 }, + { 0x3e, 0x804f, 225 }, { 0x40, 0x0050, 230 }, { 0x42, 0x8050, 235 }, + { 0x44, 0x0051, 240 }, { 0x46, 0x8051, 245 }, { 0x48, 0x0052, 251 }, + { 0x4a, 0x8052, 256 }, { 0x4c, 0x0053, 262 }, { 0x4e, 0x8053, 268 }, + { 0x50, 0x0054, 273 }, { 0x52, 0x8054, 279 }, { 0x54, 0x0055, 286 }, + { 0x56, 0x8055, 292 }, { 0x58, 0x0056, 298 }, { 0x5a, 0x8056, 305 }, + { 0x5c, 0x0057, 311 }, { 0x5e, 0x8057, 318 }, { 0x60, 0x0058, 325 }, + { 0x62, 0x8058, 332 }, { 0x64, 0x0059, 340 }, { 0x66, 0x8059, 347 }, + { 0x68, 0x005a, 355 }, { 0x6a, 0x805a, 362 }, { 0x6c, 0x005b, 370 }, + { 0x6e, 0x805b, 378 }, { 0x70, 0x005c, 387 }, { 0x72, 0x805c, 395 }, + { 0x74, 0x005d, 404 }, { 0x76, 0x805d, 413 }, { 0x78, 0x005e, 422 }, + { 0x7a, 0x805e, 431 }, { 0x7c, 0x005f, 440 }, { 0x7e, 0x805f, 450 }, + { 0x80, 0x0060, 460 }, { 0x82, 0x8060, 470 }, { 0x84, 0x0061, 480 }, + { 0x86, 0x8061, 491 }, { 0x88, 0x0062, 501 }, { 0x8a, 0x8062, 512 }, + { 0x8c, 0x0063, 524 }, { 0x8e, 0x8063, 535 }, { 0x90, 0x0064, 547 }, + { 0x92, 0x8064, 559 }, { 0x94, 0x0065, 571 }, { 0x96, 0x8065, 584 }, + { 0x98, 0x0066, 596 }, { 0x9a, 0x8066, 609 }, { 0x9c, 0x0067, 623 }, + { 0x9e, 0x8067, 636 }, { 0xa0, 0x0068, 650 }, { 0xa2, 0x8068, 665 }, + { 0xa4, 0x0069, 679 }, { 0xa6, 0x8069, 694 }, { 0xa8, 0x006a, 709 }, + { 0xaa, 0x806a, 725 }, { 0xac, 0x006b, 741 }, { 0xae, 0x806b, 757 }, + { 0xb0, 0x006c, 773 }, { 0xb2, 0x806c, 790 }, { 0xb4, 0x006d, 808 }, + { 0xb6, 0x806d, 825 }, { 0xb8, 0x006e, 843 }, { 0xba, 0x806e, 862 }, + { 0xbc, 0x006f, 881 }, { 0xbe, 0x806f, 900 }, { 0xc0, 0x0070, 920 }, + { 0xc2, 0x8070, 940 }, { 0xc4, 0x0071, 960 }, { 0xc6, 0x8071, 981 }, + { 0xc8, 0x0072, joycon_max_rumble_amp } +}; + +/* States for controller state machine */ +enum joycon_ctlr_state { + JOYCON_CTLR_STATE_INIT, + JOYCON_CTLR_STATE_READ, + JOYCON_CTLR_STATE_REMOVED, +}; + +/* Controller type received as part of device info */ +enum joycon_ctlr_type { + JOYCON_CTLR_TYPE_JCL = 0x01, + JOYCON_CTLR_TYPE_JCR = 0x02, + JOYCON_CTLR_TYPE_PRO = 0x03, + JOYCON_CTLR_TYPE_NESL = 0x09, + JOYCON_CTLR_TYPE_NESR = 0x0A, + JOYCON_CTLR_TYPE_SNES = 0x0B +}; + +struct joycon_stick_cal { + s32 max; + s32 min; + s32 center; +}; + +struct joycon_imu_cal { + s16 offset[3]; + s16 scale[3]; +}; + +/* + * All the controller's button values are stored in a u32. + * They can be accessed with bitwise ANDs. + */ +static const u32 JC_BTN_Y = BIT(0); +static const u32 JC_BTN_X = BIT(1); +static const u32 JC_BTN_B = BIT(2); +static const u32 JC_BTN_A = BIT(3); +static const u32 JC_BTN_SR_R = BIT(4); +static const u32 JC_BTN_SL_R = BIT(5); +static const u32 JC_BTN_R = BIT(6); +static const u32 JC_BTN_ZR = BIT(7); +static const u32 JC_BTN_MINUS = BIT(8); +static const u32 JC_BTN_PLUS = BIT(9); +static const u32 JC_BTN_RSTICK = BIT(10); +static const u32 JC_BTN_LSTICK = BIT(11); +static const u32 JC_BTN_HOME = BIT(12); +static const u32 JC_BTN_CAP = BIT(13); /* capture button */ +static const u32 JC_BTN_DOWN = BIT(16); +static const u32 JC_BTN_UP = BIT(17); +static const u32 JC_BTN_RIGHT = BIT(18); +static const u32 JC_BTN_LEFT = BIT(19); +static const u32 JC_BTN_SR_L = BIT(20); +static const u32 JC_BTN_SL_L = BIT(21); +static const u32 JC_BTN_L = BIT(22); +static const u32 JC_BTN_ZL = BIT(23); + +enum joycon_msg_type { + JOYCON_MSG_TYPE_NONE, + JOYCON_MSG_TYPE_USB, + JOYCON_MSG_TYPE_SUBCMD, +}; + +struct joycon_subcmd_request { + u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */ + u8 packet_num; /* incremented every send */ + u8 rumble_data[8]; + u8 subcmd_id; + u8 data[0]; /* length depends on the subcommand */ +} __packed; + +struct joycon_subcmd_reply { + u8 ack; /* MSB 1 for ACK, 0 for NACK */ + u8 id; /* id of requested subcmd */ + u8 data[0]; /* will be at most 35 bytes */ +} __packed; + +struct joycon_imu_data { + s16 accel_x; + s16 accel_y; + s16 accel_z; + s16 gyro_x; + s16 gyro_y; + s16 gyro_z; +} __packed; + +struct joycon_imu_report { + struct joycon_imu_data data[3]; +} __packed; + +struct joycon_input_report { + u8 id; + u8 timer; + u8 bat_con; /* battery and connection info */ + u8 button_status[3]; + u8 left_stick[3]; + u8 right_stick[3]; + u8 vibrator_report; + + union { + struct joycon_subcmd_reply subcmd_reply; + struct joycon_imu_report imu_report; + }; +} __packed; + +#define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35) +#define JC_NUM_LEDS 4 +#define JC_RUMBLE_DATA_SIZE 8 +#define JC_RUMBLE_QUEUE_SIZE 8 + +static const u16 JC_RUMBLE_DFLT_LOW_FREQ = 160; +static const u16 JC_RUMBLE_DFLT_HIGH_FREQ = 320; +static const u16 JC_RUMBLE_PERIOD_MS = 50; + +/* Each physical controller is associated with a joycon_ctlr struct */ +struct joycon_ctlr { + struct hid_device *hdev; + struct input_dev *input; + struct led_classdev leds[JC_NUM_LEDS]; /* player leds */ + struct led_classdev home_led; + enum joycon_ctlr_state ctlr_state; + spinlock_t lock; + u8 mac_addr[6]; + char *mac_addr_str; + enum joycon_ctlr_type ctlr_type; + + /* The following members are used for synchronous sends/receives */ + enum joycon_msg_type msg_type; + u8 subcmd_num; + struct mutex output_mutex; + u8 input_buf[JC_MAX_RESP_SIZE]; + wait_queue_head_t wait; + bool received_resp; + u8 usb_ack_match; + u8 subcmd_ack_match; + bool received_input_report; + + /* factory calibration data */ + struct joycon_stick_cal left_stick_cal_x; + struct joycon_stick_cal left_stick_cal_y; + struct joycon_stick_cal right_stick_cal_x; + struct joycon_stick_cal right_stick_cal_y; + + struct joycon_imu_cal accel_cal; + struct joycon_imu_cal gyro_cal; + + /* power supply data */ + struct power_supply *battery; + struct power_supply_desc battery_desc; + u8 battery_capacity; + bool battery_charging; + bool host_powered; + + /* rumble */ + u8 rumble_data[JC_RUMBLE_QUEUE_SIZE][JC_RUMBLE_DATA_SIZE]; + int rumble_queue_head; + int rumble_queue_tail; + struct workqueue_struct *rumble_queue; + struct work_struct rumble_worker; + unsigned int rumble_msecs; + u16 rumble_ll_freq; + u16 rumble_lh_freq; + u16 rumble_rl_freq; + u16 rumble_rh_freq; + + /* imu */ + struct input_dev *imu_input; + int timestamp; +}; + +/* Helper macros for checking controller type */ +#define jc_type_is_nescon(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR && \ + (ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESL || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESR)) +#define jc_type_is_joycon(ctlr) \ + ((ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL || \ + ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR || \ + ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP) && \ + !jc_type_is_nescon(ctlr)) +#define jc_type_is_procon(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_PROCON) +#define jc_type_is_chrggrip(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP) +#define jc_type_is_snescon(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_SNESCON) + +/* Does this controller have inputs associated with left joycon? */ +#define jc_type_has_left(ctlr) \ + (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) + +/* Does this controller have inputs associated with right joycon? */ +#define jc_type_has_right(ctlr) \ + (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) + +/* Can this controller be connected via USB */ +#define jc_has_usb(ctlr) \ + (jc_type_is_procon(ctlr) || \ + jc_type_is_chrggrip(ctlr) || \ + jc_type_is_snescon(ctlr)) + +/* Does this controller have motion sensors */ +#define jc_has_imu(ctlr) \ + (jc_type_is_joycon(ctlr) || jc_type_is_procon(ctlr)) + +static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = hid_hw_output_report(hdev, buf, len); + kfree(buf); + if (ret < 0) + hid_dbg(hdev, "Failed to send output report ret=%d\n", ret); + return ret; +} + +static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len, + u32 timeout) +{ + int ret; + int tries = 2; + + /* + * The controller occasionally seems to drop subcommands. In testing, + * doing one retry after a timeout appears to always work. + */ + while (tries--) { + /* + * If we are in the proper reporting mode, wait for an input + * report prior to sending the subcommand. This improves + * reliability considerably. + */ + if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) { + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->received_input_report = false; + spin_unlock_irqrestore(&ctlr->lock, flags); + ret = wait_event_timeout(ctlr->wait, + ctlr->received_input_report, + HZ / 4); + spin_lock_irqsave(&ctlr->lock, flags); + /* We will still proceed, even with a timeout here */ + if (!ret) + hid_warn(ctlr->hdev, + "timeout waiting for input report\n"); + spin_unlock_irqrestore(&ctlr->lock, flags); + } + + ret = __joycon_hid_send(ctlr->hdev, data, len); + if (ret < 0) { + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + return ret; + } + + ret = wait_event_timeout(ctlr->wait, ctlr->received_resp, + timeout); + if (!ret) { + hid_dbg(ctlr->hdev, + "synchronous send/receive timed out\n"); + if (tries) { + hid_dbg(ctlr->hdev, + "retrying sync send after timeout\n"); + } + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + ret = -ETIMEDOUT; + } else { + ret = 0; + break; + } + } + + ctlr->received_resp = false; + return ret; +} + +static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd, u32 timeout) +{ + int ret; + u8 buf[2] = {JC_OUTPUT_USB_CMD}; + + buf[1] = cmd; + ctlr->usb_ack_match = cmd; + ctlr->msg_type = JOYCON_MSG_TYPE_USB; + ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf), timeout); + if (ret) + hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret); + return ret; +} + +static int joycon_send_subcmd(struct joycon_ctlr *ctlr, + struct joycon_subcmd_request *subcmd, + size_t data_len, u32 timeout) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + /* + * If the controller has been removed, just return ENODEV so the LED + * subsystem doesn't print invalid errors on removal. + */ + if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) { + spin_unlock_irqrestore(&ctlr->lock, flags); + return -ENODEV; + } + memcpy(subcmd->rumble_data, ctlr->rumble_data[ctlr->rumble_queue_tail], + JC_RUMBLE_DATA_SIZE); + spin_unlock_irqrestore(&ctlr->lock, flags); + + subcmd->output_id = JC_OUTPUT_RUMBLE_AND_SUBCMD; + subcmd->packet_num = ctlr->subcmd_num; + if (++ctlr->subcmd_num > 0xF) + ctlr->subcmd_num = 0; + ctlr->subcmd_ack_match = subcmd->subcmd_id; + ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD; + + ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd, + sizeof(*subcmd) + data_len, timeout); + if (ret < 0) + hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret); + else + ret = 0; + return ret; +} + +/* Supply nibbles for flash and on. Ones correspond to active */ +static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_PLAYER_LIGHTS; + req->data[0] = (flash << 4) | on; + + hid_dbg(ctlr->hdev, "setting player leds\n"); + return joycon_send_subcmd(ctlr, req, 1, HZ/4); +} + +static const u16 DFLT_STICK_CAL_CEN = 2000; +static const u16 DFLT_STICK_CAL_MAX = 3500; +static const u16 DFLT_STICK_CAL_MIN = 500; +static int joycon_request_calibration(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 5] = { 0 }; + struct joycon_input_report *report; + struct joycon_stick_cal *cal_x; + struct joycon_stick_cal *cal_y; + s32 x_max_above; + s32 x_min_below; + s32 y_max_above; + s32 y_min_below; + u8 *data; + u8 *raw_cal; + int ret; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ; + data = req->data; + data[0] = 0xFF & JC_CAL_DATA_START; + data[1] = 0xFF & (JC_CAL_DATA_START >> 8); + data[2] = 0xFF & (JC_CAL_DATA_START >> 16); + data[3] = 0xFF & (JC_CAL_DATA_START >> 24); + data[4] = JC_CAL_DATA_SIZE; + + hid_dbg(ctlr->hdev, "requesting cal data\n"); + ret = joycon_send_subcmd(ctlr, req, 5, HZ); + if (ret) { + hid_warn(ctlr->hdev, + "Failed to read stick cal, using defaults; ret=%d\n", + ret); + + ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN; + ctlr->left_stick_cal_x.max = DFLT_STICK_CAL_MAX; + ctlr->left_stick_cal_x.min = DFLT_STICK_CAL_MIN; + + ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN; + ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX; + ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN; + + ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN; + ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX; + ctlr->right_stick_cal_x.min = DFLT_STICK_CAL_MIN; + + ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN; + ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX; + ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN; + + return ret; + } + + report = (struct joycon_input_report *)ctlr->input_buf; + raw_cal = &report->subcmd_reply.data[5]; + + /* left stick calibration parsing */ + cal_x = &ctlr->left_stick_cal_x; + cal_y = &ctlr->left_stick_cal_y; + + x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12); + y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12); + cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12); + cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12); + x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12); + y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12); + cal_x->max = cal_x->center + x_max_above; + cal_x->min = cal_x->center - x_min_below; + cal_y->max = cal_y->center + y_max_above; + cal_y->min = cal_y->center - y_min_below; + + /* right stick calibration parsing */ + raw_cal += 9; + cal_x = &ctlr->right_stick_cal_x; + cal_y = &ctlr->right_stick_cal_y; + + cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12); + cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12); + x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12); + y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12); + x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12); + y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12); + cal_x->max = cal_x->center + x_max_above; + cal_x->min = cal_x->center - x_min_below; + cal_y->max = cal_y->center + y_max_above; + cal_y->min = cal_y->center - y_min_below; + + hid_dbg(ctlr->hdev, "calibration:\n" + "l_x_c=%d l_x_max=%d l_x_min=%d\n" + "l_y_c=%d l_y_max=%d l_y_min=%d\n" + "r_x_c=%d r_x_max=%d r_x_min=%d\n" + "r_y_c=%d r_y_max=%d r_y_min=%d\n", + ctlr->left_stick_cal_x.center, + ctlr->left_stick_cal_x.max, + ctlr->left_stick_cal_x.min, + ctlr->left_stick_cal_y.center, + ctlr->left_stick_cal_y.max, + ctlr->left_stick_cal_y.min, + ctlr->right_stick_cal_x.center, + ctlr->right_stick_cal_x.max, + ctlr->right_stick_cal_x.min, + ctlr->right_stick_cal_y.center, + ctlr->right_stick_cal_y.max, + ctlr->right_stick_cal_y.min); + + return 0; +} + +static const s16 DFLT_ACCEL_OFFSET /*= 0*/; +static const s16 DFLT_ACCEL_SCALE = 16384; +static const s16 DFLT_GYRO_OFFSET /*= 0*/; +static const s16 DFLT_GYRO_SCALE = 13371; +static int joycon_request_imu_calibration(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 5] = { 0 }; + struct joycon_input_report *report; + u8 *data; + u8 *raw_cal; + int ret; + int i; + + /* request IMU calibration data */ + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ; + data = req->data; + data[0] = 0xFF & JC_IMU_CAL_DATA_START; + data[1] = 0xFF & (JC_IMU_CAL_DATA_START >> 8); + data[2] = 0xFF & (JC_IMU_CAL_DATA_START >> 16); + data[3] = 0xFF & (JC_IMU_CAL_DATA_START >> 24); + data[4] = JC_IMU_CAL_DATA_SIZE; + + hid_dbg(ctlr->hdev, "requesting IMU cal data\n"); + ret = joycon_send_subcmd(ctlr, req, 5, HZ); + + if (ret) { + hid_warn(ctlr->hdev, + "Failed to read IMU cal, using defaults; ret=%d\n", + ret); + + for (i = 0; i < 3; i++) { + ctlr->accel_cal.offset[i] = DFLT_ACCEL_OFFSET; + ctlr->accel_cal.scale[i] = DFLT_ACCEL_SCALE; + ctlr->gyro_cal.offset[i] = DFLT_GYRO_OFFSET; + ctlr->gyro_cal.scale[i] = DFLT_GYRO_SCALE; + } + return ret; + } + + report = (struct joycon_input_report *)ctlr->input_buf; + raw_cal = &report->subcmd_reply.data[5]; + + /* IMU calibration parsing */ + for (i = 0; i < 3; i++) { + int j = i * 2; + + ctlr->accel_cal.offset[i] = raw_cal[j + 0] | + ((s16)raw_cal[j + 1] << 8); + ctlr->accel_cal.scale[i] = raw_cal[j + 6] | + ((s16)raw_cal[j + 7] << 8); + ctlr->gyro_cal.offset[i] = raw_cal[j + 12] | + ((s16)raw_cal[j + 13] << 8); + ctlr->gyro_cal.scale[i] = raw_cal[j + 18] | + ((s16)raw_cal[j + 19] << 8); + } + + hid_dbg(ctlr->hdev, "IMU calibration:\n" + "a_o[0]=%d a_o[1]=%d a_o[2]=%d\n" + "a_s[0]=%d a_s[1]=%d a_s[2]=%d\n" + "g_o[0]=%d g_o[1]=%d g_o[2]=%d\n" + "g_s[0]=%d g_s[1]=%d g_s[2]=%d\n", + ctlr->accel_cal.offset[0], + ctlr->accel_cal.offset[1], + ctlr->accel_cal.offset[2], + ctlr->accel_cal.scale[0], + ctlr->accel_cal.scale[1], + ctlr->accel_cal.scale[2], + ctlr->gyro_cal.offset[0], + ctlr->gyro_cal.offset[1], + ctlr->gyro_cal.offset[2], + ctlr->gyro_cal.scale[0], + ctlr->gyro_cal.scale[1], + ctlr->gyro_cal.scale[2]); + + return 0; +} + +static int joycon_set_report_mode(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_REPORT_MODE; + req->data[0] = 0x30; /* standard, full report mode */ + + hid_dbg(ctlr->hdev, "setting controller report mode\n"); + return joycon_send_subcmd(ctlr, req, 1, HZ); +} + +static int joycon_enable_rumble(struct joycon_ctlr *ctlr, bool enable) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_ENABLE_VIBRATION; + req->data[0] = enable ? 0x01 : 0x00; + + hid_dbg(ctlr->hdev, "%s rumble\n", enable ? "enabling" : "disabling"); + return joycon_send_subcmd(ctlr, req, 1, HZ/4); +} + +static int joycon_enable_imu(struct joycon_ctlr *ctlr, bool enable) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_ENABLE_IMU; + req->data[0] = enable ? 0x01 : 0x00; + + hid_dbg(ctlr->hdev, "%s IMU\n", enable ? "enabling" : "disabling"); + return joycon_send_subcmd(ctlr, req, 1, HZ); +} + +static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val) +{ + s32 center = cal->center; + s32 min = cal->min; + s32 max = cal->max; + s32 new_val; + + if (val > center) { + new_val = (val - center) * JC_MAX_STICK_MAG; + new_val /= (max - center); + } else { + new_val = (center - val) * -JC_MAX_STICK_MAG; + new_val /= (center - min); + } + new_val = clamp(new_val, (s32)-JC_MAX_STICK_MAG, (s32)JC_MAX_STICK_MAG); + return new_val; +} + +static void joycon_parse_report(struct joycon_ctlr *ctlr, + struct joycon_input_report *rep) +{ + struct input_dev *dev = ctlr->input; + unsigned long flags; + u8 tmp; + u32 btns; + unsigned long msecs = jiffies_to_msecs(jiffies); + + spin_lock_irqsave(&ctlr->lock, flags); + if (/*IS_ENABLED(CONFIG_NINTENDO_FF) &&*/ rep->vibrator_report && + (msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS) + queue_work(ctlr->rumble_queue, &ctlr->rumble_worker); + + /* Parse the battery status */ + tmp = rep->bat_con; + ctlr->host_powered = tmp & BIT(0); + ctlr->battery_charging = tmp & BIT(4); + tmp = tmp >> 5; + switch (tmp) { + case 0: /* empty */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + break; + case 1: /* low */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + break; + case 2: /* medium */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; + case 3: /* high */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + break; + case 4: /* full */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + break; + default: + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + hid_warn(ctlr->hdev, "Invalid battery status\n"); + break; + } + spin_unlock_irqrestore(&ctlr->lock, flags); + + /* Parse the buttons and sticks */ + btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24); + + if (jc_type_has_left(ctlr)) { + u16 raw_x; + u16 raw_y; + s32 x; + s32 y; + + /* get raw stick values */ + raw_x = hid_field_extract(ctlr->hdev, rep->left_stick, 0, 12); + raw_y = hid_field_extract(ctlr->hdev, + rep->left_stick + 1, 4, 12); + /* map the stick values */ + x = joycon_map_stick_val(&ctlr->left_stick_cal_x, raw_x); + y = -joycon_map_stick_val(&ctlr->left_stick_cal_y, raw_y); + /* report sticks */ + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + + /* report buttons */ + input_report_key(dev, BTN_TL, btns & JC_BTN_L); + input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL); + if (jc_type_is_joycon(ctlr)) { + /* Report the S buttons as the non-existent triggers */ + input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L); + input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L); + } + input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS); + input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK); + input_report_key(dev, BTN_Z, btns & JC_BTN_CAP); + input_report_key(dev, BTN_DPAD_DOWN, btns & JC_BTN_DOWN); + input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP); + input_report_key(dev, BTN_DPAD_RIGHT, btns & JC_BTN_RIGHT); + input_report_key(dev, BTN_DPAD_LEFT, btns & JC_BTN_LEFT); + } + if (jc_type_has_right(ctlr)) { + u16 raw_x; + u16 raw_y; + s32 x; + s32 y; + + /* get raw stick values */ + raw_x = hid_field_extract(ctlr->hdev, rep->right_stick, 0, 12); + raw_y = hid_field_extract(ctlr->hdev, + rep->right_stick + 1, 4, 12); + /* map stick values */ + x = joycon_map_stick_val(&ctlr->right_stick_cal_x, raw_x); + y = -joycon_map_stick_val(&ctlr->right_stick_cal_y, raw_y); + /* report sticks */ + input_report_abs(dev, ABS_RX, x); + input_report_abs(dev, ABS_RY, y); + + /* report buttons */ + input_report_key(dev, BTN_TR, btns & JC_BTN_R); + input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR); + if (jc_type_is_joycon(ctlr)) { + /* Report the S buttons as the non-existent triggers */ + input_report_key(dev, BTN_TL, btns & JC_BTN_SL_R); + input_report_key(dev, BTN_TL2, btns & JC_BTN_SR_R); + } + input_report_key(dev, BTN_START, btns & JC_BTN_PLUS); + input_report_key(dev, BTN_THUMBR, btns & JC_BTN_RSTICK); + input_report_key(dev, BTN_MODE, btns & JC_BTN_HOME); + input_report_key(dev, BTN_WEST, btns & JC_BTN_Y); + input_report_key(dev, BTN_NORTH, btns & JC_BTN_X); + input_report_key(dev, BTN_EAST, btns & JC_BTN_A); + input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B); + } + + if (jc_type_is_nescon(ctlr) || jc_type_is_snescon(ctlr)) { + s8 x = 0; + s8 y = 0; + + if (btns & JC_BTN_LEFT) + x = -1; + else if (btns & JC_BTN_RIGHT) + x = 1; + + if (btns & JC_BTN_UP) + y = -1; + else if (btns & JC_BTN_DOWN) + y = 1; + + input_report_abs(dev, ABS_HAT0X, x); + input_report_abs(dev, ABS_HAT0Y, y); + + /* report buttons */ + input_report_key(dev, BTN_EAST, btns & JC_BTN_A); + input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B); + input_report_key(dev, BTN_TL, btns & JC_BTN_L); + input_report_key(dev, BTN_TR, btns & JC_BTN_R); + input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS); + input_report_key(dev, BTN_START, btns & JC_BTN_PLUS); + + if (jc_type_is_snescon(ctlr)) { + input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL); + input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR); + input_report_key(dev, BTN_NORTH, btns & JC_BTN_X); + input_report_key(dev, BTN_WEST, btns & JC_BTN_Y); + } + } + + input_sync(dev); + + /* + * Immediately after receiving a report is the most reliable time to + * send a subcommand to the controller. Wake any subcommand senders + * waiting for a report. + */ + if (unlikely(mutex_is_locked(&ctlr->output_mutex))) { + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->received_input_report = true; + spin_unlock_irqrestore(&ctlr->lock, flags); + wake_up(&ctlr->wait); + } + + /* parse IMU data if present */ + if (rep->id == JC_INPUT_IMU_DATA && jc_has_imu(ctlr)) { + struct joycon_imu_data *imu_data = rep->imu_report.data; + struct input_dev *idev = ctlr->imu_input; + int i; + int value[6]; + + for (i = 0; i < 3; i++) { /* there are 3 reports */ + ctlr->timestamp += 5000; /* each is 5 ms apart */ + input_event(idev, EV_MSC, MSC_TIMESTAMP, + ctlr->timestamp); + + /* + * Rather than convert to floats, we adjust by + * calibration factors and then multiply by the default + * scaling values. This way, the consumer only needs to + * divide by the default scale values. + */ + value[0] = (imu_data[i].gyro_x - + ctlr->gyro_cal.offset[0]) * + DFLT_GYRO_SCALE / ctlr->gyro_cal.scale[0]; + value[1] = (imu_data[i].gyro_y - + ctlr->gyro_cal.offset[1]) * + DFLT_GYRO_SCALE / ctlr->gyro_cal.scale[1]; + value[2] = (imu_data[i].gyro_z - + ctlr->gyro_cal.offset[2]) * + DFLT_GYRO_SCALE / ctlr->gyro_cal.scale[2]; + + value[3] = (imu_data[i].accel_x - + ctlr->accel_cal.offset[0]) * + DFLT_ACCEL_SCALE / ctlr->accel_cal.scale[0]; + value[4] = (imu_data[i].accel_y - + ctlr->accel_cal.offset[1]) * + DFLT_ACCEL_SCALE / ctlr->accel_cal.scale[1]; + value[5] = (imu_data[i].accel_z - + ctlr->accel_cal.offset[2]) * + DFLT_ACCEL_SCALE / ctlr->accel_cal.scale[2]; + + input_report_abs(idev, ABS_RX, value[0]); + input_report_abs(idev, ABS_RY, value[1]); + input_report_abs(idev, ABS_RZ, value[2]); + input_report_abs(idev, ABS_X, value[3]); + input_report_abs(idev, ABS_Y, value[4]); + input_report_abs(idev, ABS_Z, value[5]); + input_sync(idev); + } + } +} + +static void joycon_rumble_worker(struct work_struct *work) +{ + struct joycon_ctlr *ctlr = container_of(work, struct joycon_ctlr, + rumble_worker); + unsigned long flags; + bool again = true; + int ret; + + while (again) { + mutex_lock(&ctlr->output_mutex); + ret = joycon_enable_rumble(ctlr, true); + mutex_unlock(&ctlr->output_mutex); + + /* -ENODEV means the controller was just unplugged */ + spin_lock_irqsave(&ctlr->lock, flags); + if (ret < 0 && ret != -ENODEV && + ctlr->ctlr_state != JOYCON_CTLR_STATE_REMOVED) + hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret); + + ctlr->rumble_msecs = jiffies_to_msecs(jiffies); + if (ctlr->rumble_queue_tail != ctlr->rumble_queue_head) { + if (++ctlr->rumble_queue_tail >= JC_RUMBLE_QUEUE_SIZE) + ctlr->rumble_queue_tail = 0; + } else { + again = false; + } + spin_unlock_irqrestore(&ctlr->lock, flags); + } +} + +//#if IS_ENABLED(CONFIG_NINTENDO_FF) +static struct joycon_rumble_freq_data joycon_find_rumble_freq(u16 freq) +{ + const size_t length = ARRAY_SIZE(joycon_rumble_frequencies); + const struct joycon_rumble_freq_data *data = joycon_rumble_frequencies; + int i = 0; + + if (freq > data[0].freq) { + for (i = 1; i < length - 1; i++) { + if (freq > data[i - 1].freq && freq <= data[i].freq) + break; + } + } + + return data[i]; +} + +static struct joycon_rumble_amp_data joycon_find_rumble_amp(u16 amp) +{ + const size_t length = ARRAY_SIZE(joycon_rumble_amplitudes); + const struct joycon_rumble_amp_data *data = joycon_rumble_amplitudes; + int i = 0; + + if (amp > data[0].amp) { + for (i = 1; i < length - 1; i++) { + if (amp > data[i - 1].amp && amp <= data[i].amp) + break; + } + } + + return data[i]; +} + +static void joycon_encode_rumble(u8 *data, u16 freq_low, u16 freq_high, u16 amp) +{ + struct joycon_rumble_freq_data freq_data_low; + struct joycon_rumble_freq_data freq_data_high; + struct joycon_rumble_amp_data amp_data; + + freq_data_low = joycon_find_rumble_freq(freq_low); + freq_data_high = joycon_find_rumble_freq(freq_high); + amp_data = joycon_find_rumble_amp(amp); + + data[0] = (freq_data_high.high >> 8) & 0xFF; + data[1] = (freq_data_high.high & 0xFF) + amp_data.high; + data[2] = freq_data_low.low + ((amp_data.low >> 8) & 0xFF); + data[3] = amp_data.low & 0xFF; +} + +static const u16 JOYCON_MAX_RUMBLE_HIGH_FREQ = 1253; +static const u16 JOYCON_MIN_RUMBLE_HIGH_FREQ = 82; +static const u16 JOYCON_MAX_RUMBLE_LOW_FREQ = 626; +static const u16 JOYCON_MIN_RUMBLE_LOW_FREQ = 41; + +static void joycon_clamp_rumble_freqs(struct joycon_ctlr *ctlr) +{ + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->rumble_ll_freq = clamp(ctlr->rumble_ll_freq, + JOYCON_MIN_RUMBLE_LOW_FREQ, + JOYCON_MAX_RUMBLE_LOW_FREQ); + ctlr->rumble_lh_freq = clamp(ctlr->rumble_lh_freq, + JOYCON_MIN_RUMBLE_HIGH_FREQ, + JOYCON_MAX_RUMBLE_HIGH_FREQ); + ctlr->rumble_rl_freq = clamp(ctlr->rumble_rl_freq, + JOYCON_MIN_RUMBLE_LOW_FREQ, + JOYCON_MAX_RUMBLE_LOW_FREQ); + ctlr->rumble_rh_freq = clamp(ctlr->rumble_rh_freq, + JOYCON_MIN_RUMBLE_HIGH_FREQ, + JOYCON_MAX_RUMBLE_HIGH_FREQ); + spin_unlock_irqrestore(&ctlr->lock, flags); +} + +static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l, + bool schedule_now) +{ + u8 data[JC_RUMBLE_DATA_SIZE]; + u16 amp; + u16 freq_r_low; + u16 freq_r_high; + u16 freq_l_low; + u16 freq_l_high; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + freq_r_low = ctlr->rumble_rl_freq; + freq_r_high = ctlr->rumble_rh_freq; + freq_l_low = ctlr->rumble_ll_freq; + freq_l_high = ctlr->rumble_lh_freq; + spin_unlock_irqrestore(&ctlr->lock, flags); + + /* right joy-con */ + amp = amp_r * (u32)joycon_max_rumble_amp / 65535; + joycon_encode_rumble(data + 4, freq_r_low, freq_r_high, amp); + + /* left joy-con */ + amp = amp_l * (u32)joycon_max_rumble_amp / 65535; + joycon_encode_rumble(data, freq_l_low, freq_l_high, amp); + + spin_lock_irqsave(&ctlr->lock, flags); + if (++ctlr->rumble_queue_head >= JC_RUMBLE_QUEUE_SIZE) + ctlr->rumble_queue_head = 0; + memcpy(ctlr->rumble_data[ctlr->rumble_queue_head], data, + JC_RUMBLE_DATA_SIZE); + spin_unlock_irqrestore(&ctlr->lock, flags); + + /* don't wait for the periodic send (reduces latency) */ + if (schedule_now) + queue_work(ctlr->rumble_queue, &ctlr->rumble_worker); + + return 0; +} + +static int joycon_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct joycon_ctlr *ctlr = input_get_drvdata(dev); + + if (effect->type != FF_RUMBLE) + return 0; + + return joycon_set_rumble(ctlr, + effect->u.rumble.weak_magnitude, + effect->u.rumble.strong_magnitude, + true); +} +//#endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */ + +static const unsigned int joycon_button_inputs_l[] = { + BTN_SELECT, BTN_Z, BTN_THUMBL, + BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, + BTN_TL, BTN_TL2, + 0 /* 0 signals end of array */ +}; + +static const unsigned int joycon_button_inputs_r[] = { + BTN_START, BTN_MODE, BTN_THUMBR, + BTN_SOUTH, BTN_EAST, BTN_NORTH, BTN_WEST, + BTN_TR, BTN_TR2, + 0 /* 0 signals end of array */ +}; + +static const unsigned int nescon_button_inputs[] = { + BTN_SELECT, BTN_START, BTN_SOUTH, BTN_EAST, BTN_TL, BTN_TR, + 0 /* 0 signals end of array */ +}; + +static const unsigned int snescon_button_inputs[] = { + BTN_SELECT, BTN_START, BTN_SOUTH, BTN_EAST, BTN_NORTH, BTN_WEST, + BTN_TL, BTN_TL2, BTN_TR, BTN_TR2, + 0 /* 0 signals end of array */ +}; + +static int joycon_input_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev; + const char *name; + const char *imu_name; + int ret; + int i; + + hdev = ctlr->hdev; + + switch (hdev->product) { + case USB_DEVICE_ID_NINTENDO_PROCON: + name = "Nintendo Switch Pro Controller"; + imu_name = "Nintendo Switch Pro Controller IMU"; + break; + case USB_DEVICE_ID_NINTENDO_CHRGGRIP: + if (jc_type_has_left(ctlr)) { + name = "Nintendo Switch Left Joy-Con (Grip)"; + imu_name = "Nintendo Switch Left Joy-Con IMU (Grip)"; + } else { + name = "Nintendo Switch Right Joy-Con (Grip)"; + imu_name = "Nintendo Switch Right Joy-Con IMU (Grip)"; + } + break; + case USB_DEVICE_ID_NINTENDO_JOYCONL: + name = "Nintendo Switch Left Joy-Con"; + imu_name = "Nintendo Switch Left Joy-Con IMU"; + break; + case USB_DEVICE_ID_NINTENDO_JOYCONR: + if (ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESL) { + name = "Nintendo Switch NES Controller (L)"; + imu_name = NULL; + } else if (ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESR) { + name = "Nintendo Switch NES Controller (R)"; + imu_name = NULL; + } else { + name = "Nintendo Switch Right Joy-Con"; + imu_name = "Nintendo Switch Right Joy-Con IMU"; + } + break; + case USB_DEVICE_ID_NINTENDO_SNESCON: + name = "Nintendo Switch SNES Controller"; + imu_name = NULL; + break; + default: /* Should be impossible */ + hid_err(hdev, "Invalid hid product\n"); + return -EINVAL; + } + + ctlr->input = devm_input_allocate_device(&hdev->dev); + if (!ctlr->input) + return -ENOMEM; + ctlr->input->id.bustype = hdev->bus; + ctlr->input->id.vendor = hdev->vendor; + ctlr->input->id.product = hdev->product; + ctlr->input->id.version = hdev->version; + ctlr->input->uniq = ctlr->mac_addr_str; + ctlr->input->name = name; + input_set_drvdata(ctlr->input, ctlr); + + if (jc_type_has_left(ctlr)) { + /* set up sticks */ + input_set_abs_params(ctlr->input, ABS_X, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_Y, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + /* set up buttons */ + for (i = 0; joycon_button_inputs_l[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, + joycon_button_inputs_l[i]); + } + if (jc_type_has_right(ctlr)) { + /* set up sticks */ + input_set_abs_params(ctlr->input, ABS_RX, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_RY, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + /* set up buttons */ + for (i = 0; joycon_button_inputs_r[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, + joycon_button_inputs_r[i]); + } + if (jc_type_is_nescon(ctlr) || jc_type_is_snescon(ctlr)) { + const unsigned int* inputs = jc_type_is_nescon(ctlr) ? + nescon_button_inputs : snescon_button_inputs; + + /* set up d-pad hat */ + input_set_abs_params(ctlr->input, ABS_HAT0X, -1, 1, 0, 0); + input_set_abs_params(ctlr->input, ABS_HAT0Y, -1, 1, 0, 0); + + /* set up buttons */ + for (i = 0; inputs[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, inputs[i]); + + /* register the device here, we don't need any more setup */ + ret = input_register_device(ctlr->input); + if (ret) + return ret; + + return 0; + } + +//#if IS_ENABLED(CONFIG_NINTENDO_FF) + /* set up rumble */ + input_set_capability(ctlr->input, EV_FF, FF_RUMBLE); + input_ff_create_memless(ctlr->input, NULL, joycon_play_effect); + ctlr->rumble_ll_freq = JC_RUMBLE_DFLT_LOW_FREQ; + ctlr->rumble_lh_freq = JC_RUMBLE_DFLT_HIGH_FREQ; + ctlr->rumble_rl_freq = JC_RUMBLE_DFLT_LOW_FREQ; + ctlr->rumble_rh_freq = JC_RUMBLE_DFLT_HIGH_FREQ; + joycon_clamp_rumble_freqs(ctlr); + joycon_set_rumble(ctlr, 0, 0, false); + ctlr->rumble_msecs = jiffies_to_msecs(jiffies); +//#endif + + ret = input_register_device(ctlr->input); + if (ret) + return ret; + + /* configure the imu input device */ + ctlr->imu_input = devm_input_allocate_device(&hdev->dev); + if (!ctlr->imu_input) + return -ENOMEM; + + ctlr->imu_input->id.bustype = hdev->bus; + ctlr->imu_input->id.vendor = hdev->vendor; + ctlr->imu_input->id.product = hdev->product; + ctlr->imu_input->id.version = hdev->version; + ctlr->imu_input->uniq = ctlr->mac_addr_str; + ctlr->imu_input->name = imu_name; + input_set_drvdata(ctlr->imu_input, ctlr); + + /* configure imu axes */ + input_set_abs_params(ctlr->imu_input, ABS_X, + -JC_MAX_ACCEL_MAG, JC_MAX_ACCEL_MAG, + JC_ACCEL_FUZZ, JC_ACCEL_FLAT); + input_set_abs_params(ctlr->imu_input, ABS_Y, + -JC_MAX_ACCEL_MAG, JC_MAX_ACCEL_MAG, + JC_ACCEL_FUZZ, JC_ACCEL_FLAT); + input_set_abs_params(ctlr->imu_input, ABS_Z, + -JC_MAX_ACCEL_MAG, JC_MAX_ACCEL_MAG, + JC_ACCEL_FUZZ, JC_ACCEL_FLAT); + input_abs_set_res(ctlr->imu_input, ABS_X, JC_ACCEL_RES); + input_abs_set_res(ctlr->imu_input, ABS_Y, JC_ACCEL_RES); + input_abs_set_res(ctlr->imu_input, ABS_Z, JC_ACCEL_RES); + + input_set_abs_params(ctlr->imu_input, ABS_RX, + -JC_MAX_GYRO_MAG, JC_MAX_GYRO_MAG, + JC_GYRO_FUZZ, JC_GYRO_FLAT); + input_set_abs_params(ctlr->imu_input, ABS_RY, + -JC_MAX_GYRO_MAG, JC_MAX_GYRO_MAG, + JC_GYRO_FUZZ, JC_GYRO_FLAT); + input_set_abs_params(ctlr->imu_input, ABS_RZ, + -JC_MAX_GYRO_MAG, JC_MAX_GYRO_MAG, + JC_GYRO_FUZZ, JC_GYRO_FLAT); + + input_abs_set_res(ctlr->imu_input, ABS_RX, JC_GYRO_RES); + input_abs_set_res(ctlr->imu_input, ABS_RY, JC_GYRO_RES); + input_abs_set_res(ctlr->imu_input, ABS_RZ, JC_GYRO_RES); + + __set_bit(EV_MSC, ctlr->imu_input->evbit); + __set_bit(MSC_TIMESTAMP, ctlr->imu_input->mscbit); + __set_bit(INPUT_PROP_ACCELEROMETER, ctlr->imu_input->propbit); + + ret = input_register_device(ctlr->imu_input); + if (ret) + return ret; + + return 0; +} + +static int joycon_player_led_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = to_hid_device(dev); + struct joycon_ctlr *ctlr; + int val = 0; + int i; + int ret; + int num; + + ctlr = hid_get_drvdata(hdev); + if (!ctlr) { + hid_err(hdev, "No controller data\n"); + return -ENODEV; + } + + /* determine which player led this is */ + for (num = 0; num < JC_NUM_LEDS; num++) { + if (&ctlr->leds[num] == led) + break; + } + if (num >= JC_NUM_LEDS) + return -EINVAL; + + mutex_lock(&ctlr->output_mutex); + for (i = 0; i < JC_NUM_LEDS; i++) { + if (i == num) + val |= brightness << i; + else + val |= ctlr->leds[i].brightness << i; + } + ret = joycon_set_player_leds(ctlr, 0, val); + mutex_unlock(&ctlr->output_mutex); + + return ret; +} + +static int joycon_home_led_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = to_hid_device(dev); + struct joycon_ctlr *ctlr; + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 5] = { 0 }; + u8 *data; + int ret; + + ctlr = hid_get_drvdata(hdev); + if (!ctlr) { + hid_err(hdev, "No controller data\n"); + return -ENODEV; + } + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT; + data = req->data; + data[0] = 0x01; + data[1] = brightness << 4; + data[2] = brightness | (brightness << 4); + data[3] = 0x11; + data[4] = 0x11; + + hid_dbg(hdev, "setting home led brightness\n"); + mutex_lock(&ctlr->output_mutex); + ret = joycon_send_subcmd(ctlr, req, 5, HZ/4); + mutex_unlock(&ctlr->output_mutex); + + return ret; +} + +static const char * const joycon_player_led_names[] = { + "player1", + "player2", + "player3", + "player4" +}; + +static DEFINE_MUTEX(joycon_input_num_mutex); +static int joycon_leds_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev = ctlr->hdev; + struct device *dev = &hdev->dev; + const char *d_name = dev_name(dev); + struct led_classdev *led; + char *name; + int ret = 0; + int i; + static int input_num = 1; + + /* Set the default controller player leds based on controller number */ + mutex_lock(&joycon_input_num_mutex); + mutex_lock(&ctlr->output_mutex); + ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num)); + if (ret) + hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret); + mutex_unlock(&ctlr->output_mutex); + + /* configure the player LEDs */ + for (i = 0; i < JC_NUM_LEDS; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", d_name, + joycon_player_led_names[i]); + if (!name) + return -ENOMEM; + + led = &ctlr->leds[i]; + led->name = name; + led->brightness = ((i + 1) <= input_num) ? LED_ON : LED_OFF; + led->max_brightness = LED_ON; + led->brightness_set_blocking = + joycon_player_led_brightness_set; + led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE; + + ret = devm_led_classdev_register(&hdev->dev, led); + if (ret) { + hid_err(hdev, "Failed registering %s LED\n", led->name); + return ret; + } + } + + if (++input_num > 4) + input_num = 1; + mutex_unlock(&joycon_input_num_mutex); + + /* configure the home LED */ + if (jc_type_has_right(ctlr)) { + name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", d_name, "home"); + if (!name) + return ret; + + led = &ctlr->home_led; + led->name = name; + led->brightness = 0; + led->max_brightness = 0xF; + led->brightness_set_blocking = joycon_home_led_brightness_set; + led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE; + ret = devm_led_classdev_register(&hdev->dev, led); + if (ret) { + hid_err(hdev, "Failed registering home led\n"); + return ret; + } + /* Set the home LED to 0 as default state */ + ret = joycon_home_led_brightness_set(led, 0); + if (ret) { + hid_err(hdev, "Failed to set home LED dflt; ret=%d\n", + ret); + return ret; + } + } + + return 0; +} + +static int joycon_battery_get_property(struct power_supply *supply, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply); + unsigned long flags; + int ret = 0; + u8 capacity; + bool charging; + bool powered; + + spin_lock_irqsave(&ctlr->lock, flags); + capacity = ctlr->battery_capacity; + charging = ctlr->battery_charging; + powered = ctlr->host_powered; + spin_unlock_irqrestore(&ctlr->lock, flags); + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = capacity; + break; + case POWER_SUPPLY_PROP_STATUS: + if (charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (capacity == POWER_SUPPLY_CAPACITY_LEVEL_FULL && + powered) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static enum power_supply_property joycon_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, +}; + +static int joycon_power_supply_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev = ctlr->hdev; + struct power_supply_config supply_config = { .drv_data = ctlr, }; + const char * const name_fmt = "nintendo_switch_controller_battery_%s"; + int ret = 0; + + /* Set initially to unknown before receiving first input report */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + + /* Configure the battery's description */ + ctlr->battery_desc.properties = joycon_battery_props; + ctlr->battery_desc.num_properties = + ARRAY_SIZE(joycon_battery_props); + ctlr->battery_desc.get_property = joycon_battery_get_property; + ctlr->battery_desc.use_for_apm = 0; + ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + name_fmt, + dev_name(&hdev->dev)); + if (!ctlr->battery_desc.name) + return -ENOMEM; + + ctlr->battery = devm_power_supply_register(&hdev->dev, + &ctlr->battery_desc, + &supply_config); + if (IS_ERR(ctlr->battery)) { + ret = PTR_ERR(ctlr->battery); + hid_err(hdev, "Failed to register battery; ret=%d\n", ret); + return ret; + } + power_supply_powers(ctlr->battery, &hdev->dev); + return 0; +} + +static int joycon_read_info(struct joycon_ctlr *ctlr) +{ + int ret; + int i; + int j; + struct joycon_subcmd_request req = { 0 }; + struct joycon_input_report *report; + + req.subcmd_id = JC_SUBCMD_REQ_DEV_INFO; + ret = joycon_send_subcmd(ctlr, &req, 0, HZ); + if (ret) { + hid_err(ctlr->hdev, "Failed to get joycon info; ret=%d\n", ret); + return ret; + } + + report = (struct joycon_input_report *)ctlr->input_buf; + + for (i = 4, j = 0; j < 6; i++, j++) + ctlr->mac_addr[j] = report->subcmd_reply.data[i]; + + ctlr->mac_addr_str = devm_kasprintf(&ctlr->hdev->dev, GFP_KERNEL, + "%02X:%02X:%02X:%02X:%02X:%02X", + ctlr->mac_addr[0], + ctlr->mac_addr[1], + ctlr->mac_addr[2], + ctlr->mac_addr[3], + ctlr->mac_addr[4], + ctlr->mac_addr[5]); + if (!ctlr->mac_addr_str) + return -ENOMEM; + hid_info(ctlr->hdev, "controller MAC = %s\n", ctlr->mac_addr_str); + + /* Retrieve the type so we can distinguish for charging grip */ + ctlr->ctlr_type = report->subcmd_reply.data[2]; + + return 0; +} + +/* Common handler for parsing inputs */ +static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, + int size) +{ + int ret = 0; + + if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA || + data[0] == JC_INPUT_MCU_DATA) { + if (size >= 12) /* make sure it contains the input report */ + joycon_parse_report(ctlr, + (struct joycon_input_report *)data); + } + + return ret; +} + +static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data, + int size) +{ + int ret = 0; + bool match = false; + struct joycon_input_report *report; + + if (unlikely(mutex_is_locked(&ctlr->output_mutex)) && + ctlr->msg_type != JOYCON_MSG_TYPE_NONE) { + switch (ctlr->msg_type) { + case JOYCON_MSG_TYPE_USB: + if (size < 2) + break; + if (data[0] == JC_INPUT_USB_RESPONSE && + data[1] == ctlr->usb_ack_match) + match = true; + break; + case JOYCON_MSG_TYPE_SUBCMD: + if (size < sizeof(struct joycon_input_report) || + data[0] != JC_INPUT_SUBCMD_REPLY) + break; + report = (struct joycon_input_report *)data; + if (report->subcmd_reply.id == ctlr->subcmd_ack_match) + match = true; + break; + default: + break; + } + + if (match) { + memcpy(ctlr->input_buf, data, + min(size, (int)JC_MAX_RESP_SIZE)); + ctlr->msg_type = JOYCON_MSG_TYPE_NONE; + ctlr->received_resp = true; + wake_up(&ctlr->wait); + + /* This message has been handled */ + return 1; + } + } + + if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) + ret = joycon_ctlr_read_handler(ctlr, data, size); + + return ret; +} + +static int nintendo_hid_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + + if (size < 1) + return -EINVAL; + + return joycon_ctlr_handle_event(ctlr, raw_data, size); +} + +static int nintendo_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct joycon_ctlr *ctlr; + + hid_dbg(hdev, "probe - start\n"); + + ctlr = devm_kzalloc(&hdev->dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) { + ret = -ENOMEM; + goto err; + } + + ctlr->hdev = hdev; + ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT; + ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1; + ctlr->rumble_queue_tail = 0; + hid_set_drvdata(hdev, ctlr); + mutex_init(&ctlr->output_mutex); + init_waitqueue_head(&ctlr->wait); + spin_lock_init(&ctlr->lock); + ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq", + WQ_FREEZABLE | WQ_MEM_RECLAIM, 0); + INIT_WORK(&ctlr->rumble_worker, joycon_rumble_worker); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "HID parse failed\n"); + goto err_wq; + } + + /* + * Patch the hw version of pro controller/joycons, so applications can + * distinguish between the default HID mappings and the mappings defined + * by the Linux game controller spec. This is important for the SDL2 + * library, which has a game controller database, which uses device ids + * in combination with version as a key. + */ + hdev->version |= 0x8000; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "HW start failed\n"); + goto err_wq; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "cannot start hardware I/O\n"); + goto err_stop; + } + + hid_device_io_start(hdev); + + /* Initialize the controller */ + mutex_lock(&ctlr->output_mutex); + /* if handshake command fails, assume ble pro controller */ + if (jc_has_usb(ctlr) && !joycon_send_usb(ctlr, + JC_USB_CMD_HANDSHAKE, HZ)) { + hid_dbg(hdev, "detected USB controller\n"); + /* set baudrate for improved latency */ + ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ); + if (ret) { + hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret); + goto err_mutex; + } + /* handshake */ + ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ); + if (ret) { + hid_err(hdev, "Failed handshake; ret=%d\n", ret); + goto err_mutex; + } + /* + * Set no timeout (to keep controller in USB mode). + * This doesn't send a response, so ignore the timeout. + */ + joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10); + } else if (jc_type_is_chrggrip(ctlr)) { + hid_err(hdev, "Failed charging grip handshake\n"); + ret = -ETIMEDOUT; + goto err_mutex; + } + + /* get controller calibration data, and parse it */ + ret = joycon_request_calibration(ctlr); + if (ret) { + /* + * We can function with default calibration, but it may be + * inaccurate. Provide a warning, and continue on. + */ + hid_warn(hdev, "Analog stick positions may be inaccurate\n"); + } + + /* get IMU calibration data, and parse it */ + ret = joycon_request_imu_calibration(ctlr); + if (ret) { + /* + * We can function with default calibration, but it may be + * inaccurate. Provide a warning, and continue on. + */ + hid_warn(hdev, "Unable to read IMU calibration data\n"); + } + + /* Set the reporting mode to 0x30, which is the full report mode */ + ret = joycon_set_report_mode(ctlr); + if (ret) { + hid_err(hdev, "Failed to set report mode; ret=%d\n", ret); + goto err_mutex; + } + + + if (!jc_type_is_snescon(ctlr)) { + /* Enable rumble */ + ret = joycon_enable_rumble(ctlr, true); + if (ret) { + hid_err(hdev, + "Failed to enable rumble; ret=%d\n", ret); + goto err_mutex; + } + + /* Enable the IMU */ + ret = joycon_enable_imu(ctlr, true); + if (ret) { + hid_err(hdev, + "Failed to enable the IMU; ret=%d\n", ret); + goto err_mutex; + } + } + + ret = joycon_read_info(ctlr); + if (ret) { + hid_err(hdev, "Failed to retrieve controller info; ret=%d\n", + ret); + goto err_close; + } + + mutex_unlock(&ctlr->output_mutex); + + /* Initialize the leds */ + ret = joycon_leds_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create leds; ret=%d\n", ret); + goto err_close; + } + + /* Initialize the battery power supply */ + ret = joycon_power_supply_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); + goto err_close; + } + + ret = joycon_input_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create input device; ret=%d\n", ret); + goto err_close; + } + + ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; + + hid_dbg(hdev, "probe - success\n"); + return 0; + +err_mutex: + mutex_unlock(&ctlr->output_mutex); +err_close: + hid_hw_close(hdev); +err_stop: + hid_hw_stop(hdev); +err_wq: + destroy_workqueue(ctlr->rumble_queue); +err: + hid_err(hdev, "probe - fail = %d\n", ret); + return ret; +} + +static void nintendo_hid_remove(struct hid_device *hdev) +{ + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + unsigned long flags; + + hid_dbg(hdev, "remove\n"); + + /* Prevent further attempts at sending subcommands. */ + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->ctlr_state = JOYCON_CTLR_STATE_REMOVED; + spin_unlock_irqrestore(&ctlr->lock, flags); + + destroy_workqueue(ctlr->rumble_queue); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id nintendo_hid_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_CHRGGRIP) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_JOYCONL) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_JOYCONR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_SNESCON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_SNESCON) }, + { } +}; +MODULE_DEVICE_TABLE(hid, nintendo_hid_devices); + +static struct hid_driver nintendo_hid_driver = { + .name = "nintendo", + .id_table = nintendo_hid_devices, + .probe = nintendo_hid_probe, + .remove = nintendo_hid_remove, + .raw_event = nintendo_hid_event, +}; +module_hid_driver(nintendo_hid_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>"); +MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers"); |