From 80f1520f282e08dbeb8c811d23e7d16f483fe6af Mon Sep 17 00:00:00 2001 From: Sorgelig Date: Sat, 4 Sep 2021 21:13:47 +0800 Subject: [PATCH] hid-nintendo: support for Switch NES and SNES controllers. --- drivers/hid/hid-nintendo.c | 130 ++++++++++++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 18 deletions(-) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index ea258410a..3d5f89f2c 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -302,6 +302,9 @@ 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 { @@ -480,12 +483,19 @@ struct joycon_ctlr { }; /* 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) + ((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_snescon(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_SNESCON) #define jc_type_is_chrggrip(ctlr) \ (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP) @@ -499,6 +509,9 @@ struct joycon_ctlr { (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \ ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) +#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; @@ -1356,6 +1369,39 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, 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); /* @@ -1371,7 +1417,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_has_imu(ctlr)) joycon_parse_imu_report(ctlr, rep); } @@ -1587,6 +1633,17 @@ static const unsigned int joycon_dpad_inputs_jc[] = { BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, }; +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; @@ -1616,8 +1673,20 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) imu_name = "Nintendo Switch Left Joy-Con IMU"; break; case USB_DEVICE_ID_NINTENDO_JOYCONR: - name = "Nintendo Switch Right Joy-Con"; - imu_name = "Nintendo Switch Right Joy-Con IMU"; + 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"); @@ -1674,6 +1743,25 @@ 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_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; + } /* Let's report joy-con S triggers separately */ if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL) { @@ -2174,7 +2262,7 @@ 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)) && + if ((jc_type_is_procon(ctlr) || jc_type_is_snescon(ctlr) || jc_type_is_chrggrip(ctlr)) && !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) { hid_dbg(hdev, "detected USB controller\n"); /* set baudrate for improved latency */ @@ -2227,18 +2315,20 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_mutex; } - /* Enable rumble */ - ret = joycon_enable_rumble(ctlr); - if (ret) { - hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret); - goto err_mutex; - } + if (!jc_type_is_snescon(ctlr) && !jc_type_is_nescon(ctlr)) { + /* Enable rumble */ + ret = joycon_enable_rumble(ctlr); + if (ret) { + hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret); + goto err_mutex; + } - /* 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; + /* 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; + } } ret = joycon_read_info(ctlr); @@ -2317,6 +2407,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);