Skip to content

Commit

Permalink
HID: nintendo: add player led support
Browse files Browse the repository at this point in the history
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: Daniel J. Ogorchock <djogorchock@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
Daniel J. Ogorchock authored and Jiri Kosina committed Oct 27, 2021
1 parent 2af16c1 commit c5e6267
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 2 deletions.
2 changes: 2 additions & 0 deletions drivers/hid/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,8 @@ config HID_MULTITOUCH
config HID_NINTENDO
tristate "Nintendo Joy-Con and Pro Controller support"
depends on HID
depends on NEW_LEDS
depends on LEDS_CLASS
help
Adds support for the Nintendo Switch Joy-Cons and Pro Controller.
All controllers support bluetooth, and the Pro Controller also supports
Expand Down
97 changes: 95 additions & 2 deletions drivers/hid/hid-nintendo.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/spinlock.h>

Expand Down Expand Up @@ -184,10 +185,19 @@ struct joycon_input_report {

#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 */
struct joycon_ctlr {
struct hid_device *hdev;
struct input_dev *input;
struct led_classdev leds[JC_NUM_LEDS];
enum joycon_ctlr_state ctlr_state;

/* The following members are used for synchronous sends/receives */
Expand Down Expand Up @@ -553,11 +563,9 @@ static const unsigned int joycon_dpad_inputs_jc[] = {
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)
{
struct hid_device *hdev;
static int input_num = 1;
const char *name;
int ret;
int i;
Expand Down Expand Up @@ -643,13 +651,91 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
if (ret)
return ret;

return 0;
}

static int joycon_player_led_brightness_set(struct led_classdev *led,
enum led_brightness brightness)
{
struct device *dev = led->dev->parent;
struct hid_device *hdev = to_hid_device(dev);
struct joycon_ctlr *ctlr;
int val = 0;
int i;
int ret;
int num;

ctlr = hid_get_drvdata(hdev);
if (!ctlr) {
hid_err(hdev, "No controller data\n");
return -ENODEV;
}

/* determine which player led this is */
for (num = 0; num < JC_NUM_LEDS; num++) {
if (&ctlr->leds[num] == led)
break;
}
if (num >= JC_NUM_LEDS)
return -EINVAL;

mutex_lock(&ctlr->output_mutex);
for (i = 0; i < JC_NUM_LEDS; i++) {
if (i == num)
val |= brightness << i;
else
val |= ctlr->leds[i].brightness << i;
}
ret = joycon_set_player_leds(ctlr, 0, val);
mutex_unlock(&ctlr->output_mutex);

return ret;
}

static 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 */
mutex_lock(&joycon_input_num_mutex);
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
if (ret)
hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
mutex_unlock(&ctlr->output_mutex);

/* configure the player LEDs */
for (i = 0; i < JC_NUM_LEDS; i++) {
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%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)
input_num = 1;
mutex_unlock(&joycon_input_num_mutex);
Expand Down Expand Up @@ -813,6 +899,13 @@ static int nintendo_hid_probe(struct hid_device *hdev,

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);
if (ret) {
hid_err(hdev, "Failed to create input device; ret=%d\n", ret);
Expand Down

0 comments on commit c5e6267

Please sign in to comment.