From bdf4626b45eb631b61a55817dcf789f81f9bddb0 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 31 Jan 2020 11:43:32 +0100 Subject: [PATCH] HID: nintendo: Add support for the Nintendo Switch SNES controller. It basically acts like a Pro controller but without analog sticks. A separate mapping has been added to correctly represent its button layout and report the d-pad inputs as a hat instead ofseparate buttons. A macro to determine whether or not the controller has a USB port has also been added to make the code clearer when this is checked since there are now more possible devices. Signed-off-by: Nadia Holmquist Pedersen --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-nintendo.c | 113 +++++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 18 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 89807f826e5f3c..484a2f1ff47ca2 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -872,6 +872,7 @@ #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 diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index b86588a2ccc3ee..a2acf8f69d282f 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -412,16 +412,26 @@ struct joycon_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) + ((ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) && \ + !jc_type_is_snescon(ctlr)) /* 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) + ((ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) && \ + !jc_type_is_snescon(ctlr)) + +/* 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)) static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) { @@ -918,6 +928,37 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B); } + if (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_TL, btns & JC_BTN_L); + input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL); + input_report_key(dev, BTN_TR, btns & JC_BTN_R); + input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR); + input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS); + input_report_key(dev, BTN_START, btns & JC_BTN_PLUS); + + 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_NORTH, btns & JC_BTN_X); + input_report_key(dev, BTN_WEST, btns & JC_BTN_Y); + } + input_sync(dev); /* @@ -933,7 +974,7 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, } /* parse IMU data if present */ - if (rep->id == JC_INPUT_IMU_DATA) { + if (rep->id == JC_INPUT_IMU_DATA && !jc_type_is_snescon(ctlr)) { struct joycon_imu_data *imu_data = rep->imu_report.data; struct input_dev *idev = ctlr->imu_input; int i; @@ -1154,6 +1195,12 @@ static const unsigned int joycon_button_inputs_r[] = { 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; @@ -1186,6 +1233,10 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) 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; @@ -1228,6 +1279,23 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) input_set_capability(ctlr->input, EV_KEY, joycon_button_inputs_r[i]); } + if (jc_type_is_snescon(ctlr)) { + /* set up dpad 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; snescon_button_inputs[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, + snescon_button_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 */ @@ -1706,8 +1774,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, /* Initialize the controller */ mutex_lock(&ctlr->output_mutex); /* if handshake command fails, assume ble pro controller */ - if ((jc_type_is_procon(ctlr) || jc_type_is_chrggrip(ctlr)) && - !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) { + 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); @@ -1759,18 +1827,23 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_mutex; } - /* 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; + 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); @@ -1849,6 +1922,10 @@ static const struct hid_device_id nintendo_hid_devices[] = { 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);