2021-09-11 13:36:24 -04:00
// SPDX-License-Identifier: GPL-2.0+
/*
* HID driver for Nintendo Switch Joy - Cons and Pro Controllers
*
2021-09-11 13:36:38 -04:00
* Copyright ( c ) 2019 - 2021 Daniel J . Ogorchock < djogorchock @ gmail . com >
2021-09-11 13:36:24 -04:00
*
* 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
2021-09-11 13:36:28 -04:00
* https : //github.com/Davidobot/BetterJoyForCemu
2021-09-11 13:36:24 -04:00
* hid - wiimote kernel hid driver
* hid - logitech - hidpp driver
2021-09-11 13:36:26 -04:00
* hid - sony driver
2021-09-11 13:36:24 -04:00
*
* 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"
2021-09-11 13:36:35 -04:00
# include <asm/unaligned.h>
2021-09-11 13:36:24 -04:00
# include <linux/delay.h>
# include <linux/device.h>
2021-09-11 13:36:36 -04:00
# include <linux/kernel.h>
2021-09-11 13:36:24 -04:00
# include <linux/hid.h>
# include <linux/input.h>
2021-09-11 13:36:28 -04:00
# include <linux/jiffies.h>
2021-09-11 13:36:25 -04:00
# include <linux/leds.h>
2021-09-11 13:36:24 -04:00
# include <linux/module.h>
2021-09-11 13:36:26 -04:00
# include <linux/power_supply.h>
2021-09-11 13:36:24 -04:00
# include <linux/spinlock.h>
/*
* Reference the url below for the following HID report defines :
* https : //github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
*/
/* Output Reports */
2021-11-01 15:31:19 +01:00
# define JC_OUTPUT_RUMBLE_AND_SUBCMD 0x01
# define JC_OUTPUT_FW_UPDATE_PKT 0x03
# define JC_OUTPUT_RUMBLE_ONLY 0x10
# define JC_OUTPUT_MCU_DATA 0x11
# define JC_OUTPUT_USB_CMD 0x80
2021-09-11 13:36:24 -04:00
/* Subcommand IDs */
2021-11-01 15:31:19 +01:00
# define JC_SUBCMD_STATE 0x00
# define JC_SUBCMD_MANUAL_BT_PAIRING 0x01
# define JC_SUBCMD_REQ_DEV_INFO 0x02
# define JC_SUBCMD_SET_REPORT_MODE 0x03
# define JC_SUBCMD_TRIGGERS_ELAPSED 0x04
# define JC_SUBCMD_GET_PAGE_LIST_STATE 0x05
# define JC_SUBCMD_SET_HCI_STATE 0x06
# define JC_SUBCMD_RESET_PAIRING_INFO 0x07
# define JC_SUBCMD_LOW_POWER_MODE 0x08
# define JC_SUBCMD_SPI_FLASH_READ 0x10
# define JC_SUBCMD_SPI_FLASH_WRITE 0x11
# define JC_SUBCMD_RESET_MCU 0x20
# define JC_SUBCMD_SET_MCU_CONFIG 0x21
# define JC_SUBCMD_SET_MCU_STATE 0x22
# define JC_SUBCMD_SET_PLAYER_LIGHTS 0x30
# define JC_SUBCMD_GET_PLAYER_LIGHTS 0x31
# define JC_SUBCMD_SET_HOME_LIGHT 0x38
# define JC_SUBCMD_ENABLE_IMU 0x40
# define JC_SUBCMD_SET_IMU_SENSITIVITY 0x41
# define JC_SUBCMD_WRITE_IMU_REG 0x42
# define JC_SUBCMD_READ_IMU_REG 0x43
# define JC_SUBCMD_ENABLE_VIBRATION 0x48
# define JC_SUBCMD_GET_REGULATED_VOLTAGE 0x50
2021-09-11 13:36:24 -04:00
/* Input Reports */
2021-11-01 15:31:19 +01:00
# define JC_INPUT_BUTTON_EVENT 0x3F
# define JC_INPUT_SUBCMD_REPLY 0x21
# define JC_INPUT_IMU_DATA 0x30
# define JC_INPUT_MCU_DATA 0x31
# define JC_INPUT_USB_RESPONSE 0x81
2021-09-11 13:36:24 -04:00
/* Feature Reports */
2021-11-01 15:31:19 +01:00
# define JC_FEATURE_LAST_SUBCMD 0x02
# define JC_FEATURE_OTA_FW_UPGRADE 0x70
# define JC_FEATURE_SETUP_MEM_READ 0x71
# define JC_FEATURE_MEM_READ 0x72
# define JC_FEATURE_ERASE_MEM_SECTOR 0x73
# define JC_FEATURE_MEM_WRITE 0x74
# define JC_FEATURE_LAUNCH 0x75
2021-09-11 13:36:24 -04:00
/* USB Commands */
2021-11-01 15:31:19 +01:00
# define JC_USB_CMD_CONN_STATUS 0x01
# define JC_USB_CMD_HANDSHAKE 0x02
# define JC_USB_CMD_BAUDRATE_3M 0x03
# define JC_USB_CMD_NO_TIMEOUT 0x04
# define JC_USB_CMD_EN_TIMEOUT 0x05
# define JC_USB_RESET 0x06
# define JC_USB_PRE_HANDSHAKE 0x91
# define JC_USB_SEND_UART 0x92
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:35 -04:00
/* Magic value denoting presence of user calibration */
2021-11-01 15:31:19 +01:00
# define JC_CAL_USR_MAGIC_0 0xB2
# define JC_CAL_USR_MAGIC_1 0xA1
# define JC_CAL_USR_MAGIC_SIZE 2
2021-09-11 13:36:35 -04:00
/* SPI storage addresses of user calibration data */
2021-11-01 15:31:19 +01:00
# define JC_CAL_USR_LEFT_MAGIC_ADDR 0x8010
# define JC_CAL_USR_LEFT_DATA_ADDR 0x8012
# define JC_CAL_USR_LEFT_DATA_END 0x801A
# define JC_CAL_USR_RIGHT_MAGIC_ADDR 0x801B
# define JC_CAL_USR_RIGHT_DATA_ADDR 0x801D
2021-09-11 13:36:35 -04:00
# define JC_CAL_STICK_DATA_SIZE \
( JC_CAL_USR_LEFT_DATA_END - JC_CAL_USR_LEFT_DATA_ADDR + 1 )
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:35 -04:00
/* SPI storage addresses of factory calibration data */
2021-11-01 15:31:19 +01:00
# define JC_CAL_FCT_DATA_LEFT_ADDR 0x603d
# define JC_CAL_FCT_DATA_RIGHT_ADDR 0x6046
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:36 -04:00
/* SPI storage addresses of IMU factory calibration data */
2021-11-01 15:31:19 +01:00
# define JC_IMU_CAL_FCT_DATA_ADDR 0x6020
# define JC_IMU_CAL_FCT_DATA_END 0x6037
2021-09-11 13:36:36 -04:00
# define JC_IMU_CAL_DATA_SIZE \
( JC_IMU_CAL_FCT_DATA_END - JC_IMU_CAL_FCT_DATA_ADDR + 1 )
/* SPI storage addresses of IMU user calibration data */
2021-11-01 15:31:19 +01:00
# define JC_IMU_CAL_USR_MAGIC_ADDR 0x8026
# define JC_IMU_CAL_USR_DATA_ADDR 0x8028
2021-09-11 13:36:36 -04:00
2021-09-11 13:36:24 -04:00
/* The raw analog joystick values will be mapped in terms of this magnitude */
2021-11-01 15:31:19 +01:00
# define JC_MAX_STICK_MAG 32767
# define JC_STICK_FUZZ 250
# define JC_STICK_FLAT 500
2021-09-11 13:36:24 -04:00
/* Hat values for pro controller's d-pad */
2021-11-01 15:31:19 +01:00
# define JC_MAX_DPAD_MAG 1
# define JC_DPAD_FUZZ 0
# define JC_DPAD_FLAT 0
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:36 -04:00
/* Under most circumstances IMU reports are pushed every 15ms; use as default */
2021-11-01 15:31:19 +01:00
# define JC_IMU_DFLT_AVG_DELTA_MS 15
2021-09-11 13:36:36 -04:00
/* How many samples to sum before calculating average IMU report delta */
2021-11-01 15:31:19 +01:00
# define JC_IMU_SAMPLES_PER_DELTA_AVG 300
2021-09-11 13:36:36 -04:00
/* Controls how many dropped IMU packets at once trigger a warning message */
2021-11-01 15:31:19 +01:00
# define JC_IMU_DROPPED_PKT_WARNING 3
2021-09-11 13:36:36 -04:00
/*
* The controller ' s accelerometer has a sensor resolution of 16 bits and is
* configured with a range of + - 8000 milliGs . Therefore , the resolution can be
* calculated thus : ( 2 ^ 16 - 1 ) / ( 8000 * 2 ) = 4.096 digits per milliG
* Resolution per G ( rather than per millliG ) : 4.096 * 1000 = 4096 digits per G
* Alternatively : 1 / 4096 = .0002441 Gs per digit
*/
2021-11-01 15:31:19 +01:00
# define JC_IMU_MAX_ACCEL_MAG 32767
# define JC_IMU_ACCEL_RES_PER_G 4096
# define JC_IMU_ACCEL_FUZZ 10
# define JC_IMU_ACCEL_FLAT 0
2021-09-11 13:36:36 -04:00
/*
* The controller ' s gyroscope has a sensor resolution of 16 bits and is
* configured with a range of + - 2000 degrees / second .
* Digits per dps : ( 2 ^ 16 - 1 ) / ( 2000 * 2 ) = 16.38375
* dps per digit : 16.38375E-1 = .0610
*
* STMicro recommends in the datasheet to add 15 % to the dps / digit . This allows
* the full sensitivity range to be saturated without clipping . This yields more
* accurate results , so it ' s the technique this driver uses .
* dps per digit ( corrected ) : .0610 * 1.15 = .0702
* digits per dps ( corrected ) : .0702E-1 = 14.247
*
* Now , 14.247 truncating to 14 loses a lot of precision , so we rescale the
* min / max range by 1000.
*/
2021-11-01 15:31:19 +01:00
# define JC_IMU_PREC_RANGE_SCALE 1000
2021-09-11 13:36:36 -04:00
/* Note: change mag and res_per_dps if prec_range_scale is ever altered */
2021-11-01 15:31:19 +01:00
# define JC_IMU_MAX_GYRO_MAG 32767000 /* (2^16-1)*1000 */
# define JC_IMU_GYRO_RES_PER_DPS 14247 /* (14.247*1000) */
# define JC_IMU_GYRO_FUZZ 10
# define JC_IMU_GYRO_FLAT 0
2021-09-11 13:36:36 -04:00
2021-09-11 13:36:28 -04:00
/* 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 }
} ;
2021-09-11 13:36:24 -04:00
/* States for controller state machine */
enum joycon_ctlr_state {
JOYCON_CTLR_STATE_INIT ,
JOYCON_CTLR_STATE_READ ,
2021-09-11 13:36:31 -04:00
JOYCON_CTLR_STATE_REMOVED ,
2021-09-11 13:36:24 -04:00
} ;
2021-09-11 13:36:34 -04:00
/* 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 ,
} ;
2021-09-11 13:36:24 -04:00
struct joycon_stick_cal {
s32 max ;
s32 min ;
s32 center ;
} ;
2021-09-11 13:36:36 -04:00
struct joycon_imu_cal {
s16 offset [ 3 ] ;
s16 scale [ 3 ] ;
} ;
2021-09-11 13:36:24 -04:00
/*
* 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 ,
} ;
2021-09-11 13:36:37 -04:00
struct joycon_rumble_output {
u8 output_id ;
u8 packet_num ;
u8 rumble_data [ 8 ] ;
} __packed ;
2021-09-11 13:36:24 -04:00
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 [ ] ; /* 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 [ ] ; /* will be at most 35 bytes */
} __packed ;
2021-09-11 13:36:36 -04:00
struct joycon_imu_data {
s16 accel_x ;
s16 accel_y ;
s16 accel_z ;
s16 gyro_x ;
s16 gyro_y ;
s16 gyro_z ;
} __packed ;
2021-09-11 13:36:24 -04:00
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 ;
2021-09-11 13:36:36 -04:00
union {
struct joycon_subcmd_reply subcmd_reply ;
/* IMU input reports contain 3 samples */
u8 imu_raw_bytes [ sizeof ( struct joycon_imu_data ) * 3 ] ;
} ;
2021-09-11 13:36:24 -04:00
} __packed ;
# define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35)
2021-09-11 13:36:28 -04:00
# 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 ;
2021-09-11 13:36:39 -04:00
static const unsigned short JC_RUMBLE_ZERO_AMP_PKT_CNT = 5 ;
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:25 -04:00
static const char * const joycon_player_led_names [ ] = {
LED_FUNCTION_PLAYER1 ,
LED_FUNCTION_PLAYER2 ,
LED_FUNCTION_PLAYER3 ,
LED_FUNCTION_PLAYER4 ,
} ;
# define JC_NUM_LEDS ARRAY_SIZE(joycon_player_led_names)
2021-09-11 13:36:24 -04:00
/* Each physical controller is associated with a joycon_ctlr struct */
struct joycon_ctlr {
struct hid_device * hdev ;
struct input_dev * input ;
2021-09-11 13:36:27 -04:00
struct led_classdev leds [ JC_NUM_LEDS ] ; /* player leds */
struct led_classdev home_led ;
2021-09-11 13:36:24 -04:00
enum joycon_ctlr_state ctlr_state ;
2021-09-11 13:36:26 -04:00
spinlock_t lock ;
2021-09-11 13:36:33 -04:00
u8 mac_addr [ 6 ] ;
char * mac_addr_str ;
2021-09-11 13:36:34 -04:00
enum joycon_ctlr_type ctlr_type ;
2021-09-11 13:36:24 -04:00
/* 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 ;
2021-09-11 13:36:30 -04:00
bool received_input_report ;
2021-09-11 13:36:38 -04:00
unsigned int last_subcmd_sent_msecs ;
2021-09-11 13:36:24 -04:00
/* 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 ;
2021-09-11 13:36:36 -04:00
struct joycon_imu_cal accel_cal ;
struct joycon_imu_cal gyro_cal ;
/* prevents needlessly recalculating these divisors every sample */
s32 imu_cal_accel_divisor [ 3 ] ;
s32 imu_cal_gyro_divisor [ 3 ] ;
2021-09-11 13:36:26 -04:00
/* power supply data */
struct power_supply * battery ;
struct power_supply_desc battery_desc ;
u8 battery_capacity ;
bool battery_charging ;
bool host_powered ;
2021-09-11 13:36:28 -04:00
/* 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 ;
2021-09-11 13:36:39 -04:00
unsigned short rumble_zero_countdown ;
2021-09-11 13:36:36 -04:00
/* imu */
struct input_dev * imu_input ;
bool imu_first_packet_received ; /* helps in initiating timestamp */
unsigned int imu_timestamp_us ; /* timestamp we report to userspace */
unsigned int imu_last_pkt_ms ; /* used to calc imu report delta */
/* the following are used to track the average imu report time delta */
unsigned int imu_delta_samples_count ;
unsigned int imu_delta_samples_sum ;
unsigned int imu_avg_delta_ms ;
2021-09-11 13:36:24 -04:00
} ;
2021-09-11 13:36:34 -04:00
/* Helper macros for checking controller type */
# 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 )
# 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 )
/* 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 )
2021-09-11 13:36:24 -04:00
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 ;
}
2021-09-11 13:36:38 -04:00
static void joycon_wait_for_input_report ( struct joycon_ctlr * ctlr )
{
int ret ;
/*
* 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 ) ;
/* We will still proceed, even with a timeout here */
if ( ! ret )
hid_warn ( ctlr - > hdev ,
" timeout waiting for input report \n " ) ;
}
}
/*
* Sending subcommands and / or rumble data at too high a rate can cause bluetooth
* controller disconnections .
*/
static void joycon_enforce_subcmd_rate ( struct joycon_ctlr * ctlr )
{
static const unsigned int max_subcmd_rate_ms = 25 ;
unsigned int current_ms = jiffies_to_msecs ( jiffies ) ;
unsigned int delta_ms = current_ms - ctlr - > last_subcmd_sent_msecs ;
while ( delta_ms < max_subcmd_rate_ms & &
ctlr - > ctlr_state = = JOYCON_CTLR_STATE_READ ) {
joycon_wait_for_input_report ( ctlr ) ;
current_ms = jiffies_to_msecs ( jiffies ) ;
delta_ms = current_ms - ctlr - > last_subcmd_sent_msecs ;
}
ctlr - > last_subcmd_sent_msecs = current_ms ;
}
2021-09-11 13:36:29 -04:00
static int joycon_hid_send_sync ( struct joycon_ctlr * ctlr , u8 * data , size_t len ,
u32 timeout )
2021-09-11 13:36:24 -04:00
{
int ret ;
2021-09-11 13:36:29 -04:00
int tries = 2 ;
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:29 -04:00
/*
* The controller occasionally seems to drop subcommands . In testing ,
* doing one retry after a timeout appears to always work .
*/
while ( tries - - ) {
2021-09-11 13:36:38 -04:00
joycon_enforce_subcmd_rate ( ctlr ) ;
2021-09-11 13:36:30 -04:00
2021-09-11 13:36:29 -04:00
ret = __joycon_hid_send ( ctlr - > hdev , data , len ) ;
if ( ret < 0 ) {
memset ( ctlr - > input_buf , 0 , JC_MAX_RESP_SIZE ) ;
return ret ;
}
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:29 -04:00
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 ;
}
2021-09-11 13:36:24 -04:00
}
ctlr - > received_resp = false ;
2021-09-11 13:36:29 -04:00
return ret ;
2021-09-11 13:36:24 -04:00
}
2021-09-11 13:36:29 -04:00
static int joycon_send_usb ( struct joycon_ctlr * ctlr , u8 cmd , u32 timeout )
2021-09-11 13:36:24 -04:00
{
int ret ;
u8 buf [ 2 ] = { JC_OUTPUT_USB_CMD } ;
buf [ 1 ] = cmd ;
ctlr - > usb_ack_match = cmd ;
ctlr - > msg_type = JOYCON_MSG_TYPE_USB ;
2021-09-11 13:36:29 -04:00
ret = joycon_hid_send_sync ( ctlr , buf , sizeof ( buf ) , timeout ) ;
2021-09-11 13:36:24 -04:00
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 ,
2021-09-11 13:36:29 -04:00
size_t data_len , u32 timeout )
2021-09-11 13:36:24 -04:00
{
int ret ;
2021-09-11 13:36:28 -04:00
unsigned long flags ;
spin_lock_irqsave ( & ctlr - > lock , flags ) ;
2021-09-11 13:36:31 -04:00
/*
* 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 ;
}
2021-09-11 13:36:28 -04:00
memcpy ( subcmd - > rumble_data , ctlr - > rumble_data [ ctlr - > rumble_queue_tail ] ,
JC_RUMBLE_DATA_SIZE ) ;
spin_unlock_irqrestore ( & ctlr - > lock , flags ) ;
2021-09-11 13:36:24 -04:00
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 ,
2021-09-11 13:36:29 -04:00
sizeof ( * subcmd ) + data_len , timeout ) ;
2021-09-11 13:36:24 -04:00
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 " ) ;
2021-09-11 13:36:29 -04:00
return joycon_send_subcmd ( ctlr , req , 1 , HZ / 4 ) ;
2021-09-11 13:36:24 -04:00
}
2021-09-11 13:36:35 -04:00
static int joycon_request_spi_flash_read ( struct joycon_ctlr * ctlr ,
u32 start_addr , u8 size , u8 * * reply )
2021-09-11 13:36:24 -04:00
{
struct joycon_subcmd_request * req ;
struct joycon_input_report * report ;
2021-09-11 13:36:35 -04:00
u8 buffer [ sizeof ( * req ) + 5 ] = { 0 } ;
u8 * data ;
int ret ;
if ( ! reply )
return - EINVAL ;
req = ( struct joycon_subcmd_request * ) buffer ;
req - > subcmd_id = JC_SUBCMD_SPI_FLASH_READ ;
data = req - > data ;
put_unaligned_le32 ( start_addr , data ) ;
data [ 4 ] = size ;
hid_dbg ( ctlr - > hdev , " requesting SPI flash data \n " ) ;
ret = joycon_send_subcmd ( ctlr , req , 5 , HZ ) ;
if ( ret ) {
hid_err ( ctlr - > hdev , " failed reading SPI flash; ret=%d \n " , ret ) ;
} else {
report = ( struct joycon_input_report * ) ctlr - > input_buf ;
/* The read data starts at the 6th byte */
2021-09-11 13:36:36 -04:00
* reply = & report - > subcmd_reply . data [ 5 ] ;
2021-09-11 13:36:35 -04:00
}
return ret ;
}
/*
* User calibration ' s presence is denoted with a magic byte preceding it .
* returns 0 if magic val is present , 1 if not present , < 0 on error
*/
static int joycon_check_for_cal_magic ( struct joycon_ctlr * ctlr , u32 flash_addr )
{
int ret ;
u8 * reply ;
ret = joycon_request_spi_flash_read ( ctlr , flash_addr ,
JC_CAL_USR_MAGIC_SIZE , & reply ) ;
if ( ret )
return ret ;
return reply [ 0 ] ! = JC_CAL_USR_MAGIC_0 | | reply [ 1 ] ! = JC_CAL_USR_MAGIC_1 ;
}
static int joycon_read_stick_calibration ( struct joycon_ctlr * ctlr , u16 cal_addr ,
struct joycon_stick_cal * cal_x ,
struct joycon_stick_cal * cal_y ,
bool left_stick )
{
2021-09-11 13:36:24 -04:00
s32 x_max_above ;
s32 x_min_below ;
s32 y_max_above ;
s32 y_min_below ;
u8 * raw_cal ;
int ret ;
2021-09-11 13:36:35 -04:00
ret = joycon_request_spi_flash_read ( ctlr , cal_addr ,
JC_CAL_STICK_DATA_SIZE , & raw_cal ) ;
if ( ret )
return ret ;
/* stick calibration parsing: note the order differs based on stick */
if ( left_stick ) {
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 ) ;
} else {
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 ;
return 0 ;
}
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 )
{
u16 left_stick_addr = JC_CAL_FCT_DATA_LEFT_ADDR ;
u16 right_stick_addr = JC_CAL_FCT_DATA_RIGHT_ADDR ;
int ret ;
2021-09-11 13:36:24 -04:00
hid_dbg ( ctlr - > hdev , " requesting cal data \n " ) ;
2021-09-11 13:36:35 -04:00
/* check if user stick calibrations are present */
if ( ! joycon_check_for_cal_magic ( ctlr , JC_CAL_USR_LEFT_MAGIC_ADDR ) ) {
left_stick_addr = JC_CAL_USR_LEFT_DATA_ADDR ;
hid_info ( ctlr - > hdev , " using user cal for left stick \n " ) ;
} else {
hid_info ( ctlr - > hdev , " using factory cal for left stick \n " ) ;
}
if ( ! joycon_check_for_cal_magic ( ctlr , JC_CAL_USR_RIGHT_MAGIC_ADDR ) ) {
right_stick_addr = JC_CAL_USR_RIGHT_DATA_ADDR ;
hid_info ( ctlr - > hdev , " using user cal for right stick \n " ) ;
} else {
hid_info ( ctlr - > hdev , " using factory cal for right stick \n " ) ;
}
/* read the left stick calibration data */
ret = joycon_read_stick_calibration ( ctlr , left_stick_addr ,
& ctlr - > left_stick_cal_x ,
& ctlr - > left_stick_cal_y ,
true ) ;
2021-09-11 13:36:24 -04:00
if ( ret ) {
hid_warn ( ctlr - > hdev ,
2021-09-11 13:36:35 -04:00
" Failed to read left stick cal, using dflts; e=%d \n " ,
2021-09-11 13:36:24 -04:00
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 ;
2021-09-11 13:36:35 -04:00
}
/* read the right stick calibration data */
ret = joycon_read_stick_calibration ( ctlr , right_stick_addr ,
& ctlr - > right_stick_cal_x ,
& ctlr - > right_stick_cal_y ,
false ) ;
if ( ret ) {
hid_warn ( ctlr - > hdev ,
" Failed to read right stick cal, using dflts; e=%d \n " ,
ret ) ;
2021-09-11 13:36:24 -04:00
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 ;
}
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 ;
}
2021-09-11 13:36:36 -04:00
/*
* These divisors are calculated once rather than for each sample . They are only
* dependent on the IMU calibration values . They are used when processing the
* IMU input reports .
*/
static void joycon_calc_imu_cal_divisors ( struct joycon_ctlr * ctlr )
{
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
ctlr - > imu_cal_accel_divisor [ i ] = ctlr - > accel_cal . scale [ i ] -
ctlr - > accel_cal . offset [ i ] ;
ctlr - > imu_cal_gyro_divisor [ i ] = ctlr - > gyro_cal . scale [ i ] -
ctlr - > gyro_cal . offset [ i ] ;
}
}
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 )
{
u16 imu_cal_addr = JC_IMU_CAL_FCT_DATA_ADDR ;
u8 * raw_cal ;
int ret ;
int i ;
/* check if user calibration exists */
if ( ! joycon_check_for_cal_magic ( ctlr , JC_IMU_CAL_USR_MAGIC_ADDR ) ) {
imu_cal_addr = JC_IMU_CAL_USR_DATA_ADDR ;
hid_info ( ctlr - > hdev , " using user cal for IMU \n " ) ;
} else {
hid_info ( ctlr - > hdev , " using factory cal for IMU \n " ) ;
}
/* request IMU calibration data */
hid_dbg ( ctlr - > hdev , " requesting IMU cal data \n " ) ;
ret = joycon_request_spi_flash_read ( ctlr , imu_cal_addr ,
JC_IMU_CAL_DATA_SIZE , & raw_cal ) ;
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 ;
}
joycon_calc_imu_cal_divisors ( ctlr ) ;
return ret ;
}
/* IMU calibration parsing */
for ( i = 0 ; i < 3 ; i + + ) {
int j = i * 2 ;
ctlr - > accel_cal . offset [ i ] = get_unaligned_le16 ( raw_cal + j ) ;
ctlr - > accel_cal . scale [ i ] = get_unaligned_le16 ( raw_cal + j + 6 ) ;
ctlr - > gyro_cal . offset [ i ] = get_unaligned_le16 ( raw_cal + j + 12 ) ;
ctlr - > gyro_cal . scale [ i ] = get_unaligned_le16 ( raw_cal + j + 18 ) ;
}
joycon_calc_imu_cal_divisors ( ctlr ) ;
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 ;
}
2021-09-11 13:36:24 -04:00
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 " ) ;
2021-09-11 13:36:29 -04:00
return joycon_send_subcmd ( ctlr , req , 1 , HZ ) ;
2021-09-11 13:36:24 -04:00
}
2021-09-11 13:36:28 -04:00
static int joycon_enable_rumble ( 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_ENABLE_VIBRATION ;
req - > data [ 0 ] = 0x01 ; /* note: 0x00 would disable */
hid_dbg ( ctlr - > hdev , " enabling rumble \n " ) ;
2021-09-11 13:36:29 -04:00
return joycon_send_subcmd ( ctlr , req , 1 , HZ / 4 ) ;
2021-09-11 13:36:28 -04:00
}
2021-09-11 13:36:36 -04:00
static int joycon_enable_imu ( 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_ENABLE_IMU ;
req - > data [ 0 ] = 0x01 ; /* note: 0x00 would disable */
hid_dbg ( ctlr - > hdev , " enabling IMU \n " ) ;
return joycon_send_subcmd ( ctlr , req , 1 , HZ ) ;
}
2021-09-11 13:36:24 -04:00
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 ;
}
2021-09-11 13:36:36 -04:00
static void joycon_input_report_parse_imu_data ( struct joycon_ctlr * ctlr ,
struct joycon_input_report * rep ,
struct joycon_imu_data * imu_data )
{
u8 * raw = rep - > imu_raw_bytes ;
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
struct joycon_imu_data * data = & imu_data [ i ] ;
data - > accel_x = get_unaligned_le16 ( raw + 0 ) ;
data - > accel_y = get_unaligned_le16 ( raw + 2 ) ;
data - > accel_z = get_unaligned_le16 ( raw + 4 ) ;
data - > gyro_x = get_unaligned_le16 ( raw + 6 ) ;
data - > gyro_y = get_unaligned_le16 ( raw + 8 ) ;
data - > gyro_z = get_unaligned_le16 ( raw + 10 ) ;
/* point to next imu sample */
raw + = sizeof ( struct joycon_imu_data ) ;
}
}
static void joycon_parse_imu_report ( struct joycon_ctlr * ctlr ,
struct joycon_input_report * rep )
{
struct joycon_imu_data imu_data [ 3 ] = { 0 } ; /* 3 reports per packet */
struct input_dev * idev = ctlr - > imu_input ;
unsigned int msecs = jiffies_to_msecs ( jiffies ) ;
unsigned int last_msecs = ctlr - > imu_last_pkt_ms ;
int i ;
int value [ 6 ] ;
joycon_input_report_parse_imu_data ( ctlr , rep , imu_data ) ;
/*
* There are complexities surrounding how we determine the timestamps we
* associate with the samples we pass to userspace . The IMU input
* reports do not provide us with a good timestamp . There ' s a quickly
* incrementing 8 - bit counter per input report , but it is not very
* useful for this purpose ( it is not entirely clear what rate it
* increments at or if it varies based on packet push rate - more on
* the push rate below . . . ) .
*
* The reverse engineering work done on the joy - cons and pro controllers
* by the community seems to indicate the following :
* - The controller samples the IMU every 1.35 ms . It then does some of
* its own processing , probably averaging the samples out .
* - Each imu input report contains 3 IMU samples , ( usually 5 ms apart ) .
* - In the standard reporting mode ( which this driver uses exclusively )
* input reports are pushed from the controller as follows :
* * joy - con ( bluetooth ) : every 15 ms
* * joy - cons ( in charging grip via USB ) : every 15 ms
* * pro controller ( USB ) : every 15 ms
* * pro controller ( bluetooth ) : every 8 ms ( this is the wildcard )
*
* Further complicating matters is that some bluetooth stacks are known
* to alter the controller ' s packet rate by hardcoding the bluetooth
* SSR for the switch controllers ( android ' s stack currently sets the
* SSR to 11 ms for both the joy - cons and pro controllers ) .
*
* In my own testing , I ' ve discovered that my pro controller either
* reports IMU sample batches every 11 ms or every 15 ms . This rate is
* stable after connecting . It isn ' t 100 % clear what determines this
* rate . Importantly , even when sending every 11 ms , none of the samples
* are duplicates . This seems to indicate that the time deltas between
* reported samples can vary based on the input report rate .
*
* The solution employed in this driver is to keep track of the average
* time delta between IMU input reports . In testing , this value has
* proven to be stable , staying at 15 ms or 11 ms , though other hardware
* configurations and bluetooth stacks could potentially see other rates
* ( hopefully this will become more clear as more people use the
* driver ) .
*
* Keeping track of the average report delta allows us to submit our
* timestamps to userspace based on that . Each report contains 3
* samples , so the IMU sampling rate should be avg_time_delta / 3. We can
* also use this average to detect events where we have dropped a
* packet . The userspace timestamp for the samples will be adjusted
* accordingly to prevent unwanted behvaior .
*/
if ( ! ctlr - > imu_first_packet_received ) {
ctlr - > imu_timestamp_us = 0 ;
ctlr - > imu_delta_samples_count = 0 ;
ctlr - > imu_delta_samples_sum = 0 ;
ctlr - > imu_avg_delta_ms = JC_IMU_DFLT_AVG_DELTA_MS ;
ctlr - > imu_first_packet_received = true ;
} else {
unsigned int delta = msecs - last_msecs ;
unsigned int dropped_pkts ;
unsigned int dropped_threshold ;
/* avg imu report delta housekeeping */
ctlr - > imu_delta_samples_sum + = delta ;
ctlr - > imu_delta_samples_count + + ;
if ( ctlr - > imu_delta_samples_count > =
JC_IMU_SAMPLES_PER_DELTA_AVG ) {
ctlr - > imu_avg_delta_ms = ctlr - > imu_delta_samples_sum /
ctlr - > imu_delta_samples_count ;
/* don't ever want divide by zero shenanigans */
if ( ctlr - > imu_avg_delta_ms = = 0 ) {
ctlr - > imu_avg_delta_ms = 1 ;
hid_warn ( ctlr - > hdev ,
" calculated avg imu delta of 0 \n " ) ;
}
ctlr - > imu_delta_samples_count = 0 ;
ctlr - > imu_delta_samples_sum = 0 ;
}
/* useful for debugging IMU sample rate */
hid_dbg ( ctlr - > hdev ,
" imu_report: ms=%u last_ms=%u delta=%u avg_delta=%u \n " ,
msecs , last_msecs , delta , ctlr - > imu_avg_delta_ms ) ;
/* check if any packets have been dropped */
dropped_threshold = ctlr - > imu_avg_delta_ms * 3 / 2 ;
dropped_pkts = ( delta - min ( delta , dropped_threshold ) ) /
ctlr - > imu_avg_delta_ms ;
ctlr - > imu_timestamp_us + = 1000 * ctlr - > imu_avg_delta_ms ;
if ( dropped_pkts > JC_IMU_DROPPED_PKT_WARNING ) {
hid_warn ( ctlr - > hdev ,
" compensating for %u dropped IMU reports \n " ,
dropped_pkts ) ;
hid_warn ( ctlr - > hdev ,
" delta=%u avg_delta=%u \n " ,
delta , ctlr - > imu_avg_delta_ms ) ;
}
}
ctlr - > imu_last_pkt_ms = msecs ;
/* Each IMU input report contains three samples */
for ( i = 0 ; i < 3 ; i + + ) {
input_event ( idev , EV_MSC , MSC_TIMESTAMP ,
ctlr - > imu_timestamp_us ) ;
/*
* These calculations ( which use the controller ' s calibration
* settings to improve the final values ) are based on those
* found in the community ' s reverse - engineering repo ( linked at
* top of driver ) . For hid - nintendo , we make sure that the final
* value given to userspace is always in terms of the axis
* resolution we provided .
*
* Currently only the gyro calculations subtract the calibration
* offsets from the raw value itself . In testing , doing the same
* for the accelerometer raw values decreased accuracy .
*
* Note that the gyro values are multiplied by the
* precision - saving scaling factor to prevent large inaccuracies
* due to truncation of the resolution value which would
* otherwise occur . To prevent overflow ( without resorting to 64
* bit integer math ) , the mult_frac macro is used .
*/
value [ 0 ] = mult_frac ( ( JC_IMU_PREC_RANGE_SCALE *
( imu_data [ i ] . gyro_x -
ctlr - > gyro_cal . offset [ 0 ] ) ) ,
ctlr - > gyro_cal . scale [ 0 ] ,
ctlr - > imu_cal_gyro_divisor [ 0 ] ) ;
value [ 1 ] = mult_frac ( ( JC_IMU_PREC_RANGE_SCALE *
( imu_data [ i ] . gyro_y -
ctlr - > gyro_cal . offset [ 1 ] ) ) ,
ctlr - > gyro_cal . scale [ 1 ] ,
ctlr - > imu_cal_gyro_divisor [ 1 ] ) ;
value [ 2 ] = mult_frac ( ( JC_IMU_PREC_RANGE_SCALE *
( imu_data [ i ] . gyro_z -
ctlr - > gyro_cal . offset [ 2 ] ) ) ,
ctlr - > gyro_cal . scale [ 2 ] ,
ctlr - > imu_cal_gyro_divisor [ 2 ] ) ;
value [ 3 ] = ( ( s32 ) imu_data [ i ] . accel_x *
ctlr - > accel_cal . scale [ 0 ] ) /
ctlr - > imu_cal_accel_divisor [ 0 ] ;
value [ 4 ] = ( ( s32 ) imu_data [ i ] . accel_y *
ctlr - > accel_cal . scale [ 1 ] ) /
ctlr - > imu_cal_accel_divisor [ 1 ] ;
value [ 5 ] = ( ( s32 ) imu_data [ i ] . accel_z *
ctlr - > accel_cal . scale [ 2 ] ) /
ctlr - > imu_cal_accel_divisor [ 2 ] ;
hid_dbg ( ctlr - > hdev , " raw_gyro: g_x=%d g_y=%d g_z=%d \n " ,
imu_data [ i ] . gyro_x , imu_data [ i ] . gyro_y ,
imu_data [ i ] . gyro_z ) ;
hid_dbg ( ctlr - > hdev , " raw_accel: a_x=%d a_y=%d a_z=%d \n " ,
imu_data [ i ] . accel_x , imu_data [ i ] . accel_y ,
imu_data [ i ] . accel_z ) ;
/*
* The right joy - con has 2 axes negated , Y and Z . This is due to
* the orientation of the IMU in the controller . We negate those
* axes ' values in order to be consistent with the left joy - con
* and the pro controller :
* X : positive is pointing toward the triggers
* Y : positive is pointing to the left
* Z : positive is pointing up ( out of the buttons / sticks )
* The axes follow the right - hand rule .
*/
if ( jc_type_is_joycon ( ctlr ) & & jc_type_has_right ( ctlr ) ) {
int j ;
/* negate all but x axis */
for ( j = 1 ; j < 6 ; + + j ) {
if ( j = = 3 )
continue ;
value [ j ] * = - 1 ;
}
}
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 ) ;
/* convert to micros and divide by 3 (3 samples per report). */
ctlr - > imu_timestamp_us + = ctlr - > imu_avg_delta_ms * 1000 / 3 ;
}
}
2021-09-11 13:36:24 -04:00
static void joycon_parse_report ( struct joycon_ctlr * ctlr ,
struct joycon_input_report * rep )
{
struct input_dev * dev = ctlr - > input ;
2021-09-11 13:36:26 -04:00
unsigned long flags ;
u8 tmp ;
2021-09-11 13:36:24 -04:00
u32 btns ;
2021-09-11 13:36:28 -04:00
unsigned long msecs = jiffies_to_msecs ( jiffies ) ;
spin_lock_irqsave ( & ctlr - > lock , flags ) ;
if ( IS_ENABLED ( CONFIG_NINTENDO_FF ) & & rep - > vibrator_report & &
2021-09-11 13:36:39 -04:00
( msecs - ctlr - > rumble_msecs ) > = JC_RUMBLE_PERIOD_MS & &
( ctlr - > rumble_queue_head ! = ctlr - > rumble_queue_tail | |
ctlr - > rumble_zero_countdown > 0 ) ) {
/*
* When this value reaches 0 , we know we ' ve sent multiple
* packets to the controller instructing it to disable rumble .
* We can safely stop sending periodic rumble packets until the
* next ff effect .
*/
if ( ctlr - > rumble_zero_countdown > 0 )
ctlr - > rumble_zero_countdown - - ;
2021-09-11 13:36:28 -04:00
queue_work ( ctlr - > rumble_queue , & ctlr - > rumble_worker ) ;
2021-09-11 13:36:39 -04:00
}
2021-09-11 13:36:24 -04:00
2021-09-11 13:36:26 -04:00
/* 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 */
2021-09-11 13:36:24 -04:00
btns = hid_field_extract ( ctlr - > hdev , rep - > button_status , 0 , 24 ) ;
2021-09-11 13:36:34 -04:00
if ( jc_type_has_left ( ctlr ) ) {
2021-09-11 13:36:24 -04:00
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 ) ;
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 ) ;
2021-09-11 13:36:34 -04:00
if ( jc_type_is_joycon ( ctlr ) ) {
2021-09-11 13:36:24 -04:00
/* 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 ) ;
/* Report d-pad as digital buttons for the joy-cons */
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 ) ;
} else {
int hatx = 0 ;
int haty = 0 ;
/* d-pad x */
if ( btns & JC_BTN_LEFT )
hatx = - 1 ;
else if ( btns & JC_BTN_RIGHT )
hatx = 1 ;
input_report_abs ( dev , ABS_HAT0X , hatx ) ;
/* d-pad y */
if ( btns & JC_BTN_UP )
haty = - 1 ;
else if ( btns & JC_BTN_DOWN )
haty = 1 ;
input_report_abs ( dev , ABS_HAT0Y , haty ) ;
}
}
2021-09-11 13:36:34 -04:00
if ( jc_type_has_right ( ctlr ) ) {
2021-09-11 13:36:24 -04:00
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 ) ;
2021-09-11 13:36:34 -04:00
if ( jc_type_is_joycon ( ctlr ) ) {
2021-09-11 13:36:24 -04:00
/* 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 ) ;
}
input_sync ( dev ) ;
2021-09-11 13:36:30 -04:00
/*
* 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 ) ;
}
2021-09-11 13:36:36 -04:00
/* parse IMU data if present */
if ( rep - > id = = JC_INPUT_IMU_DATA )
joycon_parse_imu_report ( ctlr , rep ) ;
2021-09-11 13:36:24 -04:00
}
2021-09-11 13:36:37 -04:00
static int joycon_send_rumble_data ( struct joycon_ctlr * ctlr )
{
int ret ;
unsigned long flags ;
struct joycon_rumble_output rumble_output = { 0 } ;
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 ( rumble_output . rumble_data ,
ctlr - > rumble_data [ ctlr - > rumble_queue_tail ] ,
JC_RUMBLE_DATA_SIZE ) ;
spin_unlock_irqrestore ( & ctlr - > lock , flags ) ;
rumble_output . output_id = JC_OUTPUT_RUMBLE_ONLY ;
rumble_output . packet_num = ctlr - > subcmd_num ;
if ( + + ctlr - > subcmd_num > 0xF )
ctlr - > subcmd_num = 0 ;
2021-09-11 13:36:38 -04:00
joycon_enforce_subcmd_rate ( ctlr ) ;
2021-09-11 13:36:37 -04:00
ret = __joycon_hid_send ( ctlr - > hdev , ( u8 * ) & rumble_output ,
sizeof ( rumble_output ) ) ;
return ret ;
}
2021-09-11 13:36:28 -04:00
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 ) ;
2021-09-11 13:36:37 -04:00
ret = joycon_send_rumble_data ( ctlr ) ;
2021-09-11 13:36:28 -04:00
mutex_unlock ( & ctlr - > output_mutex ) ;
2021-09-11 13:36:31 -04:00
/* -ENODEV means the controller was just unplugged */
2021-09-11 13:36:28 -04:00
spin_lock_irqsave ( & ctlr - > lock , flags ) ;
2021-09-11 13:36:31 -04:00
if ( ret < 0 & & ret ! = - ENODEV & &
ctlr - > ctlr_state ! = JOYCON_CTLR_STATE_REMOVED )
hid_warn ( ctlr - > hdev , " Failed to set rumble; e=%d " , ret ) ;
2021-09-11 13:36:28 -04:00
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 ;
2021-09-11 13:36:39 -04:00
/* limit number of silent rumble packets to reduce traffic */
if ( amp_l ! = 0 | | amp_r ! = 0 )
ctlr - > rumble_zero_countdown = JC_RUMBLE_ZERO_AMP_PKT_CNT ;
2021-09-11 13:36:28 -04:00
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) */
2021-09-11 13:36:24 -04:00
static const unsigned int joycon_button_inputs_l [ ] = {
BTN_SELECT , BTN_Z , BTN_THUMBL ,
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 */
} ;
/* We report joy-con d-pad inputs as buttons and pro controller as a hat. */
static const unsigned int joycon_dpad_inputs_jc [ ] = {
BTN_DPAD_UP , BTN_DPAD_DOWN , BTN_DPAD_LEFT , BTN_DPAD_RIGHT ,
} ;
static int joycon_input_create ( struct joycon_ctlr * ctlr )
{
struct hid_device * hdev ;
const char * name ;
2021-09-11 13:36:36 -04:00
const char * imu_name ;
2021-09-11 13:36:24 -04:00
int ret ;
int i ;
hdev = ctlr - > hdev ;
switch ( hdev - > product ) {
case USB_DEVICE_ID_NINTENDO_PROCON :
name = " Nintendo Switch Pro Controller " ;
2021-09-11 13:36:36 -04:00
imu_name = " Nintendo Switch Pro Controller IMU " ;
2021-09-11 13:36:24 -04:00
break ;
2021-09-11 13:36:34 -04:00
case USB_DEVICE_ID_NINTENDO_CHRGGRIP :
2021-09-11 13:36:36 -04:00
if ( jc_type_has_left ( ctlr ) ) {
2021-09-11 13:36:34 -04:00
name = " Nintendo Switch Left Joy-Con (Grip) " ;
2021-09-11 13:36:36 -04:00
imu_name = " Nintendo Switch Left Joy-Con IMU (Grip) " ;
} else {
2021-09-11 13:36:34 -04:00
name = " Nintendo Switch Right Joy-Con (Grip) " ;
2021-09-11 13:36:36 -04:00
imu_name = " Nintendo Switch Right Joy-Con IMU (Grip) " ;
}
2021-09-11 13:36:34 -04:00
break ;
2021-09-11 13:36:24 -04:00
case USB_DEVICE_ID_NINTENDO_JOYCONL :
name = " Nintendo Switch Left Joy-Con " ;
2021-09-11 13:36:36 -04:00
imu_name = " Nintendo Switch Left Joy-Con IMU " ;
2021-09-11 13:36:24 -04:00
break ;
case USB_DEVICE_ID_NINTENDO_JOYCONR :
name = " Nintendo Switch Right Joy-Con " ;
2021-09-11 13:36:36 -04:00
imu_name = " Nintendo Switch Right Joy-Con IMU " ;
2021-09-11 13:36:24 -04:00
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 ;
2021-09-11 13:36:33 -04:00
ctlr - > input - > uniq = ctlr - > mac_addr_str ;
2021-09-11 13:36:24 -04:00
ctlr - > input - > name = name ;
input_set_drvdata ( ctlr - > input , ctlr ) ;
/* set up sticks and buttons */
2021-09-11 13:36:34 -04:00
if ( jc_type_has_left ( ctlr ) ) {
2021-09-11 13:36:24 -04:00
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 ) ;
for ( i = 0 ; joycon_button_inputs_l [ i ] > 0 ; i + + )
input_set_capability ( ctlr - > input , EV_KEY ,
joycon_button_inputs_l [ i ] ) ;
/* configure d-pad differently for joy-con vs pro controller */
if ( hdev - > product ! = USB_DEVICE_ID_NINTENDO_PROCON ) {
for ( i = 0 ; joycon_dpad_inputs_jc [ i ] > 0 ; i + + )
input_set_capability ( ctlr - > input , EV_KEY ,
joycon_dpad_inputs_jc [ i ] ) ;
} else {
input_set_abs_params ( ctlr - > input , ABS_HAT0X ,
- JC_MAX_DPAD_MAG , JC_MAX_DPAD_MAG ,
JC_DPAD_FUZZ , JC_DPAD_FLAT ) ;
input_set_abs_params ( ctlr - > input , ABS_HAT0Y ,
- JC_MAX_DPAD_MAG , JC_MAX_DPAD_MAG ,
JC_DPAD_FUZZ , JC_DPAD_FLAT ) ;
}
}
2021-09-11 13:36:34 -04:00
if ( jc_type_has_right ( ctlr ) ) {
2021-09-11 13:36:24 -04:00
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 ) ;
for ( i = 0 ; joycon_button_inputs_r [ i ] > 0 ; i + + )
input_set_capability ( ctlr - > input , EV_KEY ,
joycon_button_inputs_r [ i ] ) ;
}
/* Let's report joy-con S triggers separately */
if ( hdev - > product = = USB_DEVICE_ID_NINTENDO_JOYCONL ) {
input_set_capability ( ctlr - > input , EV_KEY , BTN_TR ) ;
input_set_capability ( ctlr - > input , EV_KEY , BTN_TR2 ) ;
} else if ( hdev - > product = = USB_DEVICE_ID_NINTENDO_JOYCONR ) {
input_set_capability ( ctlr - > input , EV_KEY , BTN_TL ) ;
input_set_capability ( ctlr - > input , EV_KEY , BTN_TL2 ) ;
}
2021-09-11 13:36:28 -04:00
# 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
2021-09-11 13:36:24 -04:00
ret = input_register_device ( ctlr - > input ) ;
if ( ret )
return ret ;
2021-09-11 13:36:36 -04:00
/* 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_IMU_MAX_ACCEL_MAG , JC_IMU_MAX_ACCEL_MAG ,
JC_IMU_ACCEL_FUZZ , JC_IMU_ACCEL_FLAT ) ;
input_set_abs_params ( ctlr - > imu_input , ABS_Y ,
- JC_IMU_MAX_ACCEL_MAG , JC_IMU_MAX_ACCEL_MAG ,
JC_IMU_ACCEL_FUZZ , JC_IMU_ACCEL_FLAT ) ;
input_set_abs_params ( ctlr - > imu_input , ABS_Z ,
- JC_IMU_MAX_ACCEL_MAG , JC_IMU_MAX_ACCEL_MAG ,
JC_IMU_ACCEL_FUZZ , JC_IMU_ACCEL_FLAT ) ;
input_abs_set_res ( ctlr - > imu_input , ABS_X , JC_IMU_ACCEL_RES_PER_G ) ;
input_abs_set_res ( ctlr - > imu_input , ABS_Y , JC_IMU_ACCEL_RES_PER_G ) ;
input_abs_set_res ( ctlr - > imu_input , ABS_Z , JC_IMU_ACCEL_RES_PER_G ) ;
input_set_abs_params ( ctlr - > imu_input , ABS_RX ,
- JC_IMU_MAX_GYRO_MAG , JC_IMU_MAX_GYRO_MAG ,
JC_IMU_GYRO_FUZZ , JC_IMU_GYRO_FLAT ) ;
input_set_abs_params ( ctlr - > imu_input , ABS_RY ,
- JC_IMU_MAX_GYRO_MAG , JC_IMU_MAX_GYRO_MAG ,
JC_IMU_GYRO_FUZZ , JC_IMU_GYRO_FLAT ) ;
input_set_abs_params ( ctlr - > imu_input , ABS_RZ ,
- JC_IMU_MAX_GYRO_MAG , JC_IMU_MAX_GYRO_MAG ,
JC_IMU_GYRO_FUZZ , JC_IMU_GYRO_FLAT ) ;
input_abs_set_res ( ctlr - > imu_input , ABS_RX , JC_IMU_GYRO_RES_PER_DPS ) ;
input_abs_set_res ( ctlr - > imu_input , ABS_RY , JC_IMU_GYRO_RES_PER_DPS ) ;
input_abs_set_res ( ctlr - > imu_input , ABS_RZ , JC_IMU_GYRO_RES_PER_DPS ) ;
__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 ;
2021-09-11 13:36:25 -04:00
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 ;
}
2021-09-11 13:36:27 -04:00
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 ) ;
2021-09-11 13:36:29 -04:00
ret = joycon_send_subcmd ( ctlr , req , 5 , HZ / 4 ) ;
2021-09-11 13:36:27 -04:00
mutex_unlock ( & ctlr - > output_mutex ) ;
return ret ;
}
2021-09-11 13:36:25 -04:00
static DEFINE_MUTEX ( joycon_input_num_mutex ) ;
2021-09-11 13:36:27 -04:00
static int joycon_leds_create ( struct joycon_ctlr * ctlr )
2021-09-11 13:36:25 -04:00
{
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 ;
2021-09-11 13:36:24 -04:00
/* 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 ) ;
2021-09-11 13:36:25 -04:00
/* configure the player LEDs */
for ( i = 0 ; i < JC_NUM_LEDS ; i + + ) {
name = devm_kasprintf ( dev , GFP_KERNEL , " %s:%s:%s " ,
d_name ,
" green " ,
joycon_player_led_names [ i ] ) ;
if ( ! name )
return - ENOMEM ;
led = & ctlr - > leds [ i ] ;
led - > name = name ;
led - > brightness = ( ( i + 1 ) < = input_num ) ? 1 : 0 ;
led - > max_brightness = 1 ;
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 ) ;
2021-09-11 13:36:27 -04:00
return ret ;
2021-09-11 13:36:25 -04:00
}
}
2021-09-11 13:36:24 -04:00
if ( + + input_num > 4 )
input_num = 1 ;
mutex_unlock ( & joycon_input_num_mutex ) ;
2021-09-11 13:36:27 -04:00
/* configure the home LED */
2021-09-11 13:36:34 -04:00
if ( jc_type_has_right ( ctlr ) ) {
2021-09-11 13:36:27 -04:00
name = devm_kasprintf ( dev , GFP_KERNEL , " %s:%s:%s " ,
d_name ,
" blue " ,
LED_FUNCTION_PLAYER5 ) ;
if ( ! name )
return - ENOMEM ;
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 ;
}
}
2021-09-11 13:36:24 -04:00
return 0 ;
}
2021-09-11 13:36:26 -04:00
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 . type = POWER_SUPPLY_TYPE_BATTERY ;
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 ;
}
return power_supply_powers ( ctlr - > battery , & hdev - > dev ) ;
}
2021-09-11 13:36:34 -04:00
static int joycon_read_info ( struct joycon_ctlr * ctlr )
2021-09-11 13:36:33 -04:00
{
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 + + )
2021-09-11 13:36:36 -04:00
ctlr - > mac_addr [ j ] = report - > subcmd_reply . data [ i ] ;
2021-09-11 13:36:33 -04:00
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 ) ;
2021-09-11 13:36:34 -04:00
/* Retrieve the type so we can distinguish for charging grip */
2021-09-11 13:36:36 -04:00
ctlr - > ctlr_type = report - > subcmd_reply . data [ 2 ] ;
2021-09-11 13:36:34 -04:00
2021-09-11 13:36:33 -04:00
return 0 ;
}
2021-09-11 13:36:24 -04:00
/* Common handler for parsing inputs */
static int joycon_ctlr_read_handler ( struct joycon_ctlr * ctlr , u8 * data ,
int size )
{
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 0 ;
}
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 ;
2021-09-11 13:36:36 -04:00
if ( report - > subcmd_reply . id = = ctlr - > subcmd_ack_match )
2021-09-11 13:36:24 -04:00
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 ;
2021-09-11 13:36:28 -04:00
ctlr - > rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1 ;
ctlr - > rumble_queue_tail = 0 ;
2021-09-11 13:36:24 -04:00
hid_set_drvdata ( hdev , ctlr ) ;
mutex_init ( & ctlr - > output_mutex ) ;
init_waitqueue_head ( & ctlr - > wait ) ;
2021-09-11 13:36:26 -04:00
spin_lock_init ( & ctlr - > lock ) ;
2021-09-11 13:36:28 -04:00
ctlr - > rumble_queue = alloc_workqueue ( " hid-nintendo-rumble_wq " ,
WQ_FREEZABLE | WQ_MEM_RECLAIM , 0 ) ;
INIT_WORK ( & ctlr - > rumble_worker , joycon_rumble_worker ) ;
2021-09-11 13:36:24 -04:00
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " HID parse failed \n " ) ;
2021-09-11 13:36:28 -04:00
goto err_wq ;
2021-09-11 13:36:24 -04:00
}
2021-09-11 13:36:32 -04:00
/*
* 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 ;
2021-09-11 13:36:24 -04:00
ret = hid_hw_start ( hdev , HID_CONNECT_HIDRAW ) ;
if ( ret ) {
hid_err ( hdev , " HW start failed \n " ) ;
2021-09-11 13:36:28 -04:00
goto err_wq ;
2021-09-11 13:36:24 -04:00
}
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 */
2021-09-11 13:36:34 -04:00
if ( ( jc_type_is_procon ( ctlr ) | | jc_type_is_chrggrip ( ctlr ) ) & &
2021-09-11 13:36:29 -04:00
! joycon_send_usb ( ctlr , JC_USB_CMD_HANDSHAKE , HZ ) ) {
2021-09-11 13:36:24 -04:00
hid_dbg ( hdev , " detected USB controller \n " ) ;
/* set baudrate for improved latency */
2021-09-11 13:36:29 -04:00
ret = joycon_send_usb ( ctlr , JC_USB_CMD_BAUDRATE_3M , HZ ) ;
2021-09-11 13:36:24 -04:00
if ( ret ) {
hid_err ( hdev , " Failed to set baudrate; ret=%d \n " , ret ) ;
goto err_mutex ;
}
/* handshake */
2021-09-11 13:36:29 -04:00
ret = joycon_send_usb ( ctlr , JC_USB_CMD_HANDSHAKE , HZ ) ;
2021-09-11 13:36:24 -04:00
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 .
*/
2021-09-11 13:36:29 -04:00
joycon_send_usb ( ctlr , JC_USB_CMD_NO_TIMEOUT , HZ / 10 ) ;
2021-09-11 13:36:34 -04:00
} else if ( jc_type_is_chrggrip ( ctlr ) ) {
hid_err ( hdev , " Failed charging grip handshake \n " ) ;
ret = - ETIMEDOUT ;
goto err_mutex ;
2021-09-11 13:36:24 -04:00
}
/* 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 " ) ;
}
2021-09-11 13:36:36 -04:00
/* 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 " ) ;
}
2021-09-11 13:36:24 -04:00
/* 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 ;
}
2021-09-11 13:36:28 -04:00
/* Enable rumble */
ret = joycon_enable_rumble ( ctlr ) ;
if ( ret ) {
hid_err ( hdev , " Failed to enable rumble; ret=%d \n " , ret ) ;
goto err_mutex ;
}
2021-09-11 13:36:36 -04:00
/* Enable the IMU */
ret = joycon_enable_imu ( ctlr ) ;
if ( ret ) {
hid_err ( hdev , " Failed to enable the IMU; ret=%d \n " , ret ) ;
goto err_mutex ;
}
2021-09-11 13:36:34 -04:00
ret = joycon_read_info ( ctlr ) ;
2021-09-11 13:36:33 -04:00
if ( ret ) {
2021-09-11 13:36:34 -04:00
hid_err ( hdev , " Failed to retrieve controller info; ret=%d \n " ,
2021-09-11 13:36:33 -04:00
ret ) ;
goto err_mutex ;
}
2021-09-11 13:36:24 -04:00
mutex_unlock ( & ctlr - > output_mutex ) ;
2021-09-11 13:36:25 -04:00
/* Initialize the leds */
2021-09-11 13:36:27 -04:00
ret = joycon_leds_create ( ctlr ) ;
2021-09-11 13:36:25 -04:00
if ( ret ) {
hid_err ( hdev , " Failed to create leds; ret=%d \n " , ret ) ;
goto err_close ;
}
2021-09-11 13:36:26 -04:00
/* 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 ;
}
2021-09-11 13:36:24 -04:00
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 ) ;
2021-09-11 13:36:28 -04:00
err_wq :
destroy_workqueue ( ctlr - > rumble_queue ) ;
2021-09-11 13:36:24 -04:00
err :
hid_err ( hdev , " probe - fail = %d \n " , ret ) ;
return ret ;
}
static void nintendo_hid_remove ( struct hid_device * hdev )
{
2021-09-11 13:36:28 -04:00
struct joycon_ctlr * ctlr = hid_get_drvdata ( hdev ) ;
2021-09-11 13:36:31 -04:00
unsigned long flags ;
2021-09-11 13:36:28 -04:00
2021-09-11 13:36:24 -04:00
hid_dbg ( hdev , " remove \n " ) ;
2021-09-11 13:36:31 -04:00
/* 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 ) ;
2021-09-11 13:36:28 -04:00
destroy_workqueue ( ctlr - > rumble_queue ) ;
2021-09-11 13:36:31 -04:00
2021-09-11 13:36:24 -04:00
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 ) } ,
2021-09-11 13:36:34 -04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_NINTENDO ,
USB_DEVICE_ID_NINTENDO_CHRGGRIP ) } ,
2021-09-11 13:36:24 -04:00
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_NINTENDO ,
USB_DEVICE_ID_NINTENDO_JOYCONL ) } ,
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_NINTENDO ,
USB_DEVICE_ID_NINTENDO_JOYCONR ) } ,
{ }
} ;
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 " ) ;
2021-09-11 13:36:36 -04:00