Commit c5e62676 authored by Daniel J. Ogorchock's avatar Daniel J. Ogorchock Committed by Jiri Kosina

HID: nintendo: add player led support

This patch adds led_classdev functionality to the switch controller
driver. It adds support for the 4 player LEDs. The Home Button LED still
needs to be supported on the pro controllers and right joy-con.
Signed-off-by: default avatarDaniel J. Ogorchock <djogorchock@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 2af16c1f
...@@ -734,6 +734,8 @@ config HID_MULTITOUCH ...@@ -734,6 +734,8 @@ config HID_MULTITOUCH
config HID_NINTENDO config HID_NINTENDO
tristate "Nintendo Joy-Con and Pro Controller support" tristate "Nintendo Joy-Con and Pro Controller support"
depends on HID depends on HID
depends on NEW_LEDS
depends on LEDS_CLASS
help help
Adds support for the Nintendo Switch Joy-Cons and Pro Controller. Adds support for the Nintendo Switch Joy-Cons and Pro Controller.
All controllers support bluetooth, and the Pro Controller also supports All controllers support bluetooth, and the Pro Controller also supports
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -184,10 +185,19 @@ struct joycon_input_report { ...@@ -184,10 +185,19 @@ struct joycon_input_report {
#define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35) #define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35)
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)
/* Each physical controller is associated with a joycon_ctlr struct */ /* Each physical controller is associated with a joycon_ctlr struct */
struct joycon_ctlr { struct joycon_ctlr {
struct hid_device *hdev; struct hid_device *hdev;
struct input_dev *input; struct input_dev *input;
struct led_classdev leds[JC_NUM_LEDS];
enum joycon_ctlr_state ctlr_state; enum joycon_ctlr_state ctlr_state;
/* The following members are used for synchronous sends/receives */ /* The following members are used for synchronous sends/receives */
...@@ -553,11 +563,9 @@ static const unsigned int joycon_dpad_inputs_jc[] = { ...@@ -553,11 +563,9 @@ static const unsigned int joycon_dpad_inputs_jc[] = {
BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT,
}; };
static DEFINE_MUTEX(joycon_input_num_mutex);
static int joycon_input_create(struct joycon_ctlr *ctlr) static int joycon_input_create(struct joycon_ctlr *ctlr)
{ {
struct hid_device *hdev; struct hid_device *hdev;
static int input_num = 1;
const char *name; const char *name;
int ret; int ret;
int i; int i;
...@@ -643,6 +651,59 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) ...@@ -643,6 +651,59 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
if (ret) if (ret)
return ret; return ret;
return 0;
}
static int joycon_player_led_brightness_set(struct led_classdev *led,
enum led_brightness brightness)
{
struct device *dev = led->dev->parent;
struct hid_device *hdev = to_hid_device(dev);
struct joycon_ctlr *ctlr;
int val = 0;
int i;
int ret;
int num;
ctlr = hid_get_drvdata(hdev);
if (!ctlr) {
hid_err(hdev, "No controller data\n");
return -ENODEV;
}
/* determine which player led this is */
for (num = 0; num < JC_NUM_LEDS; num++) {
if (&ctlr->leds[num] == led)
break;
}
if (num >= JC_NUM_LEDS)
return -EINVAL;
mutex_lock(&ctlr->output_mutex);
for (i = 0; i < JC_NUM_LEDS; i++) {
if (i == num)
val |= brightness << i;
else
val |= ctlr->leds[i].brightness << i;
}
ret = joycon_set_player_leds(ctlr, 0, val);
mutex_unlock(&ctlr->output_mutex);
return ret;
}
static DEFINE_MUTEX(joycon_input_num_mutex);
static int joycon_player_leds_create(struct joycon_ctlr *ctlr)
{
struct hid_device *hdev = ctlr->hdev;
struct device *dev = &hdev->dev;
const char *d_name = dev_name(dev);
struct led_classdev *led;
char *name;
int ret = 0;
int i;
static int input_num = 1;
/* Set the default controller player leds based on controller number */ /* Set the default controller player leds based on controller number */
mutex_lock(&joycon_input_num_mutex); mutex_lock(&joycon_input_num_mutex);
mutex_lock(&ctlr->output_mutex); mutex_lock(&ctlr->output_mutex);
...@@ -650,6 +711,31 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) ...@@ -650,6 +711,31 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
if (ret) if (ret)
hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret); hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
mutex_unlock(&ctlr->output_mutex); mutex_unlock(&ctlr->output_mutex);
/* configure the player LEDs */
for (i = 0; i < JC_NUM_LEDS; i++) {
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%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);
break;
}
}
if (++input_num > 4) if (++input_num > 4)
input_num = 1; input_num = 1;
mutex_unlock(&joycon_input_num_mutex); mutex_unlock(&joycon_input_num_mutex);
...@@ -813,6 +899,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, ...@@ -813,6 +899,13 @@ static int nintendo_hid_probe(struct hid_device *hdev,
mutex_unlock(&ctlr->output_mutex); mutex_unlock(&ctlr->output_mutex);
/* Initialize the leds */
ret = joycon_player_leds_create(ctlr);
if (ret) {
hid_err(hdev, "Failed to create leds; ret=%d\n", ret);
goto err_close;
}
ret = joycon_input_create(ctlr); ret = joycon_input_create(ctlr);
if (ret) { if (ret) {
hid_err(hdev, "Failed to create input device; ret=%d\n", ret); hid_err(hdev, "Failed to create input device; ret=%d\n", ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment