Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
a6be4c6c
Commit
a6be4c6c
authored
Nov 05, 2021
by
Jiri Kosina
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-5.16/playstation' into for-linus
- LED handling improvements (Roderick Colenbrander)
parents
b026277a
d7f1f9fe
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
180 additions
and
1 deletion
+180
-1
Documentation/leds/well-known-leds.txt
Documentation/leds/well-known-leds.txt
+14
-0
drivers/hid/Kconfig
drivers/hid/Kconfig
+1
-0
drivers/hid/hid-playstation.c
drivers/hid/hid-playstation.c
+158
-1
include/dt-bindings/leds/common.h
include/dt-bindings/leds/common.h
+7
-0
No files found.
Documentation/leds/well-known-leds.txt
View file @
a6be4c6c
...
@@ -16,6 +16,20 @@ but then try the legacy ones, too.
...
@@ -16,6 +16,20 @@ but then try the legacy ones, too.
Notice there's a list of functions in include/dt-bindings/leds/common.h .
Notice there's a list of functions in include/dt-bindings/leds/common.h .
* Gamepads and joysticks
Game controllers may feature LEDs to indicate a player number. This is commonly
used on game consoles in which multiple controllers can be connected to a system.
The "player LEDs" are then programmed with a pattern to indicate a particular
player. For example, a game controller with 4 LEDs, may be programmed with "x---"
to indicate player 1, "-x--" to indicate player 2 etcetera where "x" means on.
Input drivers can utilize the LED class to expose the individual player LEDs
of a game controller using the function "player".
Note: tracking and management of Player IDs is the responsibility of user space,
though drivers may pick a default value.
Good: "input*:*:player-{1,2,3,4,5}
* Keyboards
* Keyboards
Good: "input*:*:capslock"
Good: "input*:*:capslock"
...
...
drivers/hid/Kconfig
View file @
a6be4c6c
...
@@ -868,6 +868,7 @@ config HID_PLANTRONICS
...
@@ -868,6 +868,7 @@ config HID_PLANTRONICS
config HID_PLAYSTATION
config HID_PLAYSTATION
tristate "PlayStation HID Driver"
tristate "PlayStation HID Driver"
depends on HID
depends on HID
depends on LEDS_CLASS_MULTICOLOR
select CRC32
select CRC32
select POWER_SUPPLY
select POWER_SUPPLY
help
help
...
...
drivers/hid/hid-playstation.c
View file @
a6be4c6c
...
@@ -11,6 +11,8 @@
...
@@ -11,6 +11,8 @@
#include <linux/hid.h>
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/idr.h>
#include <linux/input/mt.h>
#include <linux/input/mt.h>
#include <linux/leds.h>
#include <linux/led-class-multicolor.h>
#include <linux/module.h>
#include <linux/module.h>
#include <asm/unaligned.h>
#include <asm/unaligned.h>
...
@@ -38,6 +40,7 @@ struct ps_device {
...
@@ -38,6 +40,7 @@ struct ps_device {
uint8_t
battery_capacity
;
uint8_t
battery_capacity
;
int
battery_status
;
int
battery_status
;
const
char
*
input_dev_name
;
/* Name of primary input device. */
uint8_t
mac_address
[
6
];
/* Note: stored in little endian order. */
uint8_t
mac_address
[
6
];
/* Note: stored in little endian order. */
uint32_t
hw_version
;
uint32_t
hw_version
;
uint32_t
fw_version
;
uint32_t
fw_version
;
...
@@ -53,6 +56,13 @@ struct ps_calibration_data {
...
@@ -53,6 +56,13 @@ struct ps_calibration_data {
int
sens_denom
;
int
sens_denom
;
};
};
struct
ps_led_info
{
const
char
*
name
;
const
char
*
color
;
enum
led_brightness
(
*
brightness_get
)(
struct
led_classdev
*
cdev
);
int
(
*
brightness_set
)(
struct
led_classdev
*
cdev
,
enum
led_brightness
);
};
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
#define PS_INPUT_CRC32_SEED 0xA1
#define PS_INPUT_CRC32_SEED 0xA1
#define PS_OUTPUT_CRC32_SEED 0xA2
#define PS_OUTPUT_CRC32_SEED 0xA2
...
@@ -147,6 +157,7 @@ struct dualsense {
...
@@ -147,6 +157,7 @@ struct dualsense {
uint8_t
motor_right
;
uint8_t
motor_right
;
/* RGB lightbar */
/* RGB lightbar */
struct
led_classdev_mc
lightbar
;
bool
update_lightbar
;
bool
update_lightbar
;
uint8_t
lightbar_red
;
uint8_t
lightbar_red
;
uint8_t
lightbar_green
;
uint8_t
lightbar_green
;
...
@@ -288,6 +299,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
...
@@ -288,6 +299,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
{
0
,
0
},
{
0
,
0
},
};
};
static
void
dualsense_set_lightbar
(
struct
dualsense
*
ds
,
uint8_t
red
,
uint8_t
green
,
uint8_t
blue
);
/*
/*
* Add a new ps_device to ps_devices if it doesn't exist.
* Add a new ps_device to ps_devices if it doesn't exist.
* Return error on duplicate device, which can happen if the same
* Return error on duplicate device, which can happen if the same
...
@@ -525,6 +538,71 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu
...
@@ -525,6 +538,71 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu
return
0
;
return
0
;
}
}
static
int
ps_led_register
(
struct
ps_device
*
ps_dev
,
struct
led_classdev
*
led
,
const
struct
ps_led_info
*
led_info
)
{
int
ret
;
led
->
name
=
devm_kasprintf
(
&
ps_dev
->
hdev
->
dev
,
GFP_KERNEL
,
"%s:%s:%s"
,
ps_dev
->
input_dev_name
,
led_info
->
color
,
led_info
->
name
);
if
(
!
led
->
name
)
return
-
ENOMEM
;
led
->
brightness
=
0
;
led
->
max_brightness
=
1
;
led
->
flags
=
LED_CORE_SUSPENDRESUME
;
led
->
brightness_get
=
led_info
->
brightness_get
;
led
->
brightness_set_blocking
=
led_info
->
brightness_set
;
ret
=
devm_led_classdev_register
(
&
ps_dev
->
hdev
->
dev
,
led
);
if
(
ret
)
{
hid_err
(
ps_dev
->
hdev
,
"Failed to register LED %s: %d
\n
"
,
led_info
->
name
,
ret
);
return
ret
;
}
return
0
;
}
/* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */
static
int
ps_lightbar_register
(
struct
ps_device
*
ps_dev
,
struct
led_classdev_mc
*
lightbar_mc_dev
,
int
(
*
brightness_set
)(
struct
led_classdev
*
,
enum
led_brightness
))
{
struct
hid_device
*
hdev
=
ps_dev
->
hdev
;
struct
mc_subled
*
mc_led_info
;
struct
led_classdev
*
led_cdev
;
int
ret
;
mc_led_info
=
devm_kmalloc_array
(
&
hdev
->
dev
,
3
,
sizeof
(
*
mc_led_info
),
GFP_KERNEL
|
__GFP_ZERO
);
if
(
!
mc_led_info
)
return
-
ENOMEM
;
mc_led_info
[
0
].
color_index
=
LED_COLOR_ID_RED
;
mc_led_info
[
1
].
color_index
=
LED_COLOR_ID_GREEN
;
mc_led_info
[
2
].
color_index
=
LED_COLOR_ID_BLUE
;
lightbar_mc_dev
->
subled_info
=
mc_led_info
;
lightbar_mc_dev
->
num_colors
=
3
;
led_cdev
=
&
lightbar_mc_dev
->
led_cdev
;
led_cdev
->
name
=
devm_kasprintf
(
&
hdev
->
dev
,
GFP_KERNEL
,
"%s:rgb:indicator"
,
ps_dev
->
input_dev_name
);
if
(
!
led_cdev
->
name
)
return
-
ENOMEM
;
led_cdev
->
brightness
=
255
;
led_cdev
->
max_brightness
=
255
;
led_cdev
->
brightness_set_blocking
=
brightness_set
;
ret
=
devm_led_classdev_multicolor_register
(
&
hdev
->
dev
,
lightbar_mc_dev
);
if
(
ret
<
0
)
{
hid_err
(
hdev
,
"Cannot register multicolor LED device
\n
"
);
return
ret
;
}
return
0
;
}
static
struct
input_dev
*
ps_sensors_create
(
struct
hid_device
*
hdev
,
int
accel_range
,
int
accel_res
,
static
struct
input_dev
*
ps_sensors_create
(
struct
hid_device
*
hdev
,
int
accel_range
,
int
accel_res
,
int
gyro_range
,
int
gyro_res
)
int
gyro_range
,
int
gyro_res
)
{
{
...
@@ -761,6 +839,53 @@ static int dualsense_get_mac_address(struct dualsense *ds)
...
@@ -761,6 +839,53 @@ static int dualsense_get_mac_address(struct dualsense *ds)
return
ret
;
return
ret
;
}
}
static
int
dualsense_lightbar_set_brightness
(
struct
led_classdev
*
cdev
,
enum
led_brightness
brightness
)
{
struct
led_classdev_mc
*
mc_cdev
=
lcdev_to_mccdev
(
cdev
);
struct
dualsense
*
ds
=
container_of
(
mc_cdev
,
struct
dualsense
,
lightbar
);
uint8_t
red
,
green
,
blue
;
led_mc_calc_color_components
(
mc_cdev
,
brightness
);
red
=
mc_cdev
->
subled_info
[
0
].
brightness
;
green
=
mc_cdev
->
subled_info
[
1
].
brightness
;
blue
=
mc_cdev
->
subled_info
[
2
].
brightness
;
dualsense_set_lightbar
(
ds
,
red
,
green
,
blue
);
return
0
;
}
static
enum
led_brightness
dualsense_player_led_get_brightness
(
struct
led_classdev
*
led
)
{
struct
hid_device
*
hdev
=
to_hid_device
(
led
->
dev
->
parent
);
struct
dualsense
*
ds
=
hid_get_drvdata
(
hdev
);
return
!!
(
ds
->
player_leds_state
&
BIT
(
led
-
ds
->
player_leds
));
}
static
int
dualsense_player_led_set_brightness
(
struct
led_classdev
*
led
,
enum
led_brightness
value
)
{
struct
hid_device
*
hdev
=
to_hid_device
(
led
->
dev
->
parent
);
struct
dualsense
*
ds
=
hid_get_drvdata
(
hdev
);
unsigned
long
flags
;
unsigned
int
led_index
;
spin_lock_irqsave
(
&
ds
->
base
.
lock
,
flags
);
led_index
=
led
-
ds
->
player_leds
;
if
(
value
==
LED_OFF
)
ds
->
player_leds_state
&=
~
BIT
(
led_index
);
else
ds
->
player_leds_state
|=
BIT
(
led_index
);
ds
->
update_player_leds
=
true
;
spin_unlock_irqrestore
(
&
ds
->
base
.
lock
,
flags
);
schedule_work
(
&
ds
->
output_worker
);
return
0
;
}
static
void
dualsense_init_output_report
(
struct
dualsense
*
ds
,
struct
dualsense_output_report
*
rp
,
static
void
dualsense_init_output_report
(
struct
dualsense
*
ds
,
struct
dualsense_output_report
*
rp
,
void
*
buf
)
void
*
buf
)
{
{
...
@@ -1106,10 +1231,14 @@ static int dualsense_reset_leds(struct dualsense *ds)
...
@@ -1106,10 +1231,14 @@ static int dualsense_reset_leds(struct dualsense *ds)
static
void
dualsense_set_lightbar
(
struct
dualsense
*
ds
,
uint8_t
red
,
uint8_t
green
,
uint8_t
blue
)
static
void
dualsense_set_lightbar
(
struct
dualsense
*
ds
,
uint8_t
red
,
uint8_t
green
,
uint8_t
blue
)
{
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
ds
->
base
.
lock
,
flags
);
ds
->
update_lightbar
=
true
;
ds
->
update_lightbar
=
true
;
ds
->
lightbar_red
=
red
;
ds
->
lightbar_red
=
red
;
ds
->
lightbar_green
=
green
;
ds
->
lightbar_green
=
green
;
ds
->
lightbar_blue
=
blue
;
ds
->
lightbar_blue
=
blue
;
spin_unlock_irqrestore
(
&
ds
->
base
.
lock
,
flags
);
schedule_work
(
&
ds
->
output_worker
);
schedule_work
(
&
ds
->
output_worker
);
}
}
...
@@ -1142,7 +1271,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
...
@@ -1142,7 +1271,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
struct
dualsense
*
ds
;
struct
dualsense
*
ds
;
struct
ps_device
*
ps_dev
;
struct
ps_device
*
ps_dev
;
uint8_t
max_output_report_size
;
uint8_t
max_output_report_size
;
int
ret
;
int
i
,
ret
;
static
const
struct
ps_led_info
player_leds_info
[]
=
{
{
LED_FUNCTION_PLAYER1
,
"white"
,
dualsense_player_led_get_brightness
,
dualsense_player_led_set_brightness
},
{
LED_FUNCTION_PLAYER2
,
"white"
,
dualsense_player_led_get_brightness
,
dualsense_player_led_set_brightness
},
{
LED_FUNCTION_PLAYER3
,
"white"
,
dualsense_player_led_get_brightness
,
dualsense_player_led_set_brightness
},
{
LED_FUNCTION_PLAYER4
,
"white"
,
dualsense_player_led_get_brightness
,
dualsense_player_led_set_brightness
},
{
LED_FUNCTION_PLAYER5
,
"white"
,
dualsense_player_led_get_brightness
,
dualsense_player_led_set_brightness
}
};
ds
=
devm_kzalloc
(
&
hdev
->
dev
,
sizeof
(
*
ds
),
GFP_KERNEL
);
ds
=
devm_kzalloc
(
&
hdev
->
dev
,
sizeof
(
*
ds
),
GFP_KERNEL
);
if
(
!
ds
)
if
(
!
ds
)
...
@@ -1196,6 +1338,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
...
@@ -1196,6 +1338,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
ret
=
PTR_ERR
(
ds
->
gamepad
);
ret
=
PTR_ERR
(
ds
->
gamepad
);
goto
err
;
goto
err
;
}
}
/* Use gamepad input device name as primary device name for e.g. LEDs */
ps_dev
->
input_dev_name
=
dev_name
(
&
ds
->
gamepad
->
dev
);
ds
->
sensors
=
ps_sensors_create
(
hdev
,
DS_ACC_RANGE
,
DS_ACC_RES_PER_G
,
ds
->
sensors
=
ps_sensors_create
(
hdev
,
DS_ACC_RANGE
,
DS_ACC_RES_PER_G
,
DS_GYRO_RANGE
,
DS_GYRO_RES_PER_DEG_S
);
DS_GYRO_RANGE
,
DS_GYRO_RES_PER_DEG_S
);
...
@@ -1223,8 +1367,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
...
@@ -1223,8 +1367,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
if
(
ret
)
if
(
ret
)
goto
err
;
goto
err
;
ret
=
ps_lightbar_register
(
ps_dev
,
&
ds
->
lightbar
,
dualsense_lightbar_set_brightness
);
if
(
ret
)
goto
err
;
/* Set default lightbar color. */
dualsense_set_lightbar
(
ds
,
0
,
0
,
128
);
/* blue */
dualsense_set_lightbar
(
ds
,
0
,
0
,
128
);
/* blue */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
player_leds_info
);
i
++
)
{
const
struct
ps_led_info
*
led_info
=
&
player_leds_info
[
i
];
ret
=
ps_led_register
(
ps_dev
,
&
ds
->
player_leds
[
i
],
led_info
);
if
(
ret
<
0
)
goto
err
;
}
ret
=
ps_device_set_player_id
(
ps_dev
);
ret
=
ps_device_set_player_id
(
ps_dev
);
if
(
ret
)
{
if
(
ret
)
{
hid_err
(
hdev
,
"Failed to assign player id for DualSense: %d
\n
"
,
ret
);
hid_err
(
hdev
,
"Failed to assign player id for DualSense: %d
\n
"
,
ret
);
...
...
include/dt-bindings/leds/common.h
View file @
a6be4c6c
...
@@ -60,6 +60,13 @@
...
@@ -60,6 +60,13 @@
#define LED_FUNCTION_MICMUTE "micmute"
#define LED_FUNCTION_MICMUTE "micmute"
#define LED_FUNCTION_MUTE "mute"
#define LED_FUNCTION_MUTE "mute"
/* Used for player LEDs as found on game controllers from e.g. Nintendo, Sony. */
#define LED_FUNCTION_PLAYER1 "player-1"
#define LED_FUNCTION_PLAYER2 "player-2"
#define LED_FUNCTION_PLAYER3 "player-3"
#define LED_FUNCTION_PLAYER4 "player-4"
#define LED_FUNCTION_PLAYER5 "player-5"
/* Miscelleaus functions. Use functions above if you can. */
/* Miscelleaus functions. Use functions above if you can. */
#define LED_FUNCTION_ACTIVITY "activity"
#define LED_FUNCTION_ACTIVITY "activity"
#define LED_FUNCTION_ALARM "alarm"
#define LED_FUNCTION_ALARM "alarm"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment