From nobody Fri Sep 11 15:50:54 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hhJtD0jFjz6sY71 for ; Fri, 11 Sep 2026 15:51:00 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hhJtC6k5fz4Zj3 for ; Fri, 11 Sep 2026 15:50:59 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1789141859; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Wfi1RskHC/qXUtnYoU1R+X6Zwv6PmZVWm9wOmLYMkcc=; b=PLDl6zUQWSrRNf7jgr+JbedSH3G6IZ0nEUlDjCHbvOnjH9+Ym/jHXDOoHuZ+WnXXK9/+F9 YyIf+hNtwj3s2dRAt5BX82Z69Hx4Ew7uwA+kdz+S/erIL2DbAOEjI2xOChNTtGbUelx+th AO56zkpYNkW3F0F+dqA+e/1HkIh6S3VdSS9b74AsKtdc1a1TgctgSRiF5PA+HWSDdyWRbp YQNUUxj9tHXTHEQ8T74/XwPQD3nbJ1JtV03pcpRZb6oEeLkA+e/eIZZt7dlnHcSOsbZRWY mFdisk4ZNMsBR8JuiuO3YlJEtIM1Solk+L83Y4Lno+u9UqVUPhnT9dESRz8Sgw== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1789141860; b=UGA4ZYxiGNl2UlNjbrqs5BaepK8OtxFPIRIFJwJyGoD41x8ywcIXSnD+x2t5Y96jf2vqY/ OW2Bh3uYhEuCXY3gfzMRN7gNCUyb2EbKs52qCG346fpxxHd+sotakQTXojDxocJuHqbTtd KJg2TwgMwl7RKtV3SqslolorZgx5nTtr0i2jh5rbymVkVM1sSeXA80uTw5lL6L+2Zzsu2A x63kQoiTUBWYd7DEgRc3k8to4DBtl8jY05DxMsOnPDWbEtbTUlZXeh+d8mEuWVPmo5+9+e sVKLzl66e3wZbEAv+EpSaBmhsGuRvJdbanP7wRCWSIlLcqN7PpNJGXzeq/54mw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1789141860; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Wfi1RskHC/qXUtnYoU1R+X6Zwv6PmZVWm9wOmLYMkcc=; b=VJts+1VWbB6eV6xamc1VzkrbTx5XQuFXjwBPlKuzDKIyRO/zBVAOAkPwHgzO8vr/TDnO54 rOb9s7BoYTpY3SUnmyJZlE5CrY2Ir8PMH3HBhx7/YxEt14kldCipkXVRGXywBpuSQ1jTMa vpVUgUygkYgIuHxR8W9JZg83yDLSW2cV0aaWvFDShLggTo5YV1XDAZJrm2sNEAtjKnMhEM VJ/7u6RQ+Oy2GiUnW6m+l1EEoESDn2syTFIzwGfbLUHBObjApcvGXsUgadf7SAxA/JoWg1 oL42Gdw/ytRXm3LAriicVc0Qt7AsRWKGYjYkZoXt89aWKDYhVuRKFse9tVgtZQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hhJtC5btYzKJC for ; Fri, 11 Sep 2026 15:50:59 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3a7fe by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 11 Sep 2026 15:50:54 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Christos Longros From: Vladimir Kondratyev Subject: git: d8e315ba9751 - main - hid: Add Sony DualSense gamepad driver List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: wulf X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d8e315ba975163a2050dd3d3e193f2472dbc2478 Auto-Submitted: auto-generated Date: Fri, 11 Sep 2026 15:50:54 +0000 Message-Id: <6aa4235e.3a7fe.2d48954a@gitrepo.freebsd.org> The branch main has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=d8e315ba975163a2050dd3d3e193f2472dbc2478 commit d8e315ba975163a2050dd3d3e193f2472dbc2478 Author: Christos Longros AuthorDate: 2026-09-11 15:49:22 +0000 Commit: Vladimir Kondratyev CommitDate: 2026-09-11 15:49:22 +0000 hid: Add Sony DualSense gamepad driver Add a driver for the Sony PS5 DualSense controller (054c:0ce6) providing gamepad input via evdev, lightbar RGB LED control, and player indicator LEDs through sysctl. Signed-off-by: Christos Longros Reviewed by: ziaee, wulf MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D56345 --- share/man/man4/Makefile | 1 + share/man/man4/ps5dsense.4 | 145 ++++++++++++ sys/conf/NOTES | 1 + sys/conf/files | 1 + sys/dev/hid/ps5dsense.c | 472 +++++++++++++++++++++++++++++++++++++ sys/modules/hid/Makefile | 1 + sys/modules/hid/ps5dsense/Makefile | 8 + 7 files changed, 629 insertions(+) diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 305597d8bedb..75b9b7cefd92 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -496,6 +496,7 @@ MAN= aac.4 \ procfs.4 \ proto.4 \ ps4dshock.4 \ + ps5dsense.4 \ psm.4 \ pst.4 \ pt.4 \ diff --git a/share/man/man4/ps5dsense.4 b/share/man/man4/ps5dsense.4 new file mode 100644 index 000000000000..8e412b8bde9a --- /dev/null +++ b/share/man/man4/ps5dsense.4 @@ -0,0 +1,145 @@ +.\" +.\" Copyright (c) 2026 Christos Longros +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd August 29, 2026 +.Dt PS5DSENSE 4 +.Os +.Sh NAME +.Nm ps5dsense +.Nd Sony PlayStation 5 DualSense gamepad driver +.Sh SYNOPSIS +.Cd device ps5dsense +.Cd device hid +.Cd device hidbus +.Cd device hidmap +.Cd device evdev +.Pp +In +.Xr sysctl.conf 5 : +.Cd dev.ps5dsense.%d.led_state +.Cd dev.ps5dsense.%d.led_color_r +.Cd dev.ps5dsense.%d.led_color_g +.Cd dev.ps5dsense.%d.led_color_b +.Cd dev.ps5dsense.%d.player_leds +.Pp +In +.Xr loader.conf 5 : +.Cd hw.hid.ps5dsense.debug +.Cd ps5dsense_load +.Sh DESCRIPTION +The +.Nm +driver supports the Sony PlayStation 5 DualSense gamepad +connected over USB, +and presents it to applications over the +.Sy evdev +interface. +.Pp +If the appropriate hardware is detected, +the driver will be loaded automatically by +.Xr devmatch 8 . +To load the driver manually at boot time, set the +.Va ps5dsense_load +variable to +.Ar YES +at the +.Xr loader 8 +prompt, or add it to +.Xr loader.conf 5 . +.Pp +To give user applications access to the game controller, +allow user access to the +.Pa /dev/input/event* +nodes with inclusion of user in the +.Em games +group, +or with a +.Xr devfs.rules 5 +ruleset. +.Pp +The microphone mute button is reported as +.Dv KEY_MICMUTE ; +unlike the vendor driver on other systems, +.Nm +does not mute the built-in microphone itself. +.Sh HARDWARE +The +.Nm +driver supports the following devices: +.Pp +.Bl -bullet -compact +.It +Sony DualSense wireless controller +.Pq USB product ID 0x0ce6 +.El +.Sh SYSCTL VARIABLES +The following variables are available as +.Xr sysctl 8 +variables: +.Bl -tag -width indent +.It Va dev.ps5dsense.%d.led_state +Lightbar state, +where 0 is off and +1 is on. +.It Va dev.ps5dsense.%d.led_color_r +Lightbar color, +red component. +.It Va dev.ps5dsense.%d.led_color_g +Lightbar color, +green component. +.It Va dev.ps5dsense.%d.led_color_b +Lightbar color, +blue component. +.It Va dev.ps5dsense.%d.player_leds +Player indicator LEDs, +where -1 is off and +0 to 4 select the player number. +.El +.Pp +The following variable is available as both +.Xr sysctl 8 +variable and +.Xr loader 8 +tunable: +.Bl -tag -width indent +.It Va hw.hid.ps5dsense.debug +Debug output level, +where 0 is debugging disabled and +larger values increase debug message verbosity. +Default is 0. +.El +.Sh FILES +.Bl -tag -width "/dev/input/event*" -compact +.It Pa /dev/input/event* +input event device +.Pq Sy evdev +node +.El +.Sh SEE ALSO +.Xr hgame 4 , +.Xr ps4dshock 4 , +.Xr usbhid 4 , +.Xr devfs.rules 5 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 16.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Christos Longros Aq Mt chris.longros@gmail.com . +.Sh BUGS +The +.Nm +driver does not support force-feedback events, haptic feedback or the +adaptive triggers. +.Pp +The touchpad and the motion sensors are not decoded. +The touchpad click is therefore not reported at all. +.Pp +Bluetooth operation is not supported. diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 11af716a8e08..d405de62bed7 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2441,6 +2441,7 @@ device hpen # Generic pen driver device hrfkill # RFKILL driver device hsctrl # System controls device ps4dshock # Sony PS4 DualShock 4 gamepad driver +device ps5dsense # Sony PS5 DualSense gamepad driver device u2f # FIDO/U2F authenticator options U2F_DROP_UHID_ALIAS # Do not install /dev/uhid alias for # /dev/u2f/ and rename driver from uhid to u2f diff --git a/sys/conf/files b/sys/conf/files index c6655459187f..a195d93cb169 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1816,6 +1816,7 @@ dev/hid/hrfkill.c optional hrfkill dev/hid/hsctrl.c optional hsctrl dev/hid/ietp.c optional ietp dev/hid/ps4dshock.c optional ps4dshock +dev/hid/ps5dsense.c optional ps5dsense dev/hid/u2f.c optional u2f dev/hid/xb360gp.c optional xb360gp dev/hptiop/hptiop.c optional hptiop scbus diff --git a/sys/dev/hid/ps5dsense.c b/sys/dev/hid/ps5dsense.c new file mode 100644 index 000000000000..75933debe223 --- /dev/null +++ b/sys/dev/hid/ps5dsense.c @@ -0,0 +1,472 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Vladimir Kondratyev + * Copyright (c) 2026 Christos Longros + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Sony PS5 DualSense driver + * https://controllers.fandom.com/wiki/Sony_DualSense + * https://github.com/torvalds/linux/blob/master/drivers/hid/hid-playstation.c + * + * Touchpad and motion sensors are not supported yet. + */ + +#include "opt_hid.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define HID_DEBUG_VAR ps5ds_debug +#include +#include +#include +#include +#include +#include "usbdevs.h" + +#ifdef HID_DEBUG +static int ps5ds_debug = 1; + +static SYSCTL_NODE(_hw_hid, OID_AUTO, ps5dsense, CTLFLAG_RW, 0, + "Sony PS5 DualSense Gamepad"); +SYSCTL_INT(_hw_hid_ps5dsense, OID_AUTO, debug, CTLFLAG_RWTUN, + &ps5ds_debug, 0, "Debug level"); +#endif + +/* + * Fixed HID report descriptor. Output is 62 bytes, not 47: shorter reports + * are ignored. + */ +static const uint8_t ps5ds_rdesc[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x09, 0x05, /* Usage (Game Pad) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x01, /* Report ID (1) */ + 0x09, 0x30, /* Usage (X) - left stick X */ + 0x09, 0x31, /* Usage (Y) - left stick Y */ + 0x09, 0x33, /* Usage (Rx) - right stick X */ + 0x09, 0x34, /* Usage (Ry) - right stick Y */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x04, /* Report Count (4) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x09, 0x32, /* Usage (Z) - L2 trigger */ + 0x09, 0x35, /* Usage (Rz) - R2 trigger */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */ + 0x09, 0x20, /* Usage (0x20) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x01, /* Report Count (1) */ + 0x81, 0x02, /* Input (Data,Var,Abs) - counter */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x39, /* Usage (Hat switch) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x07, /* Logical Maximum (7) */ + 0x35, 0x00, /* Physical Minimum (0) */ + 0x46, 0x3B, 0x01, /* Physical Maximum (315) */ + 0x65, 0x14, /* Unit (Eng Rot: Degree) */ + 0x75, 0x04, /* Report Size (4) */ + 0x95, 0x01, /* Report Count (1) */ + 0x81, 0x42, /* Input (Data,Var,Abs,Null State) */ + 0x65, 0x00, /* Unit (None) */ + 0x45, 0x00, /* Physical Maximum (0) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (0x01) */ + 0x29, 0x0F, /* Usage Maximum (0x0F) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x0F, /* Report Count (15) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */ + 0x09, 0x21, /* Usage (0x21) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x0D, /* Report Count (13) */ + 0x81, 0x03, /* Input (Const) - unused button bits */ + 0x09, 0x22, /* Usage (0x22) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x34, /* Report Count (52) */ + 0x81, 0x03, /* Input (Const) - sensors, touchpad */ + /* Output report (63 bytes total) for LED and rumble control */ + 0x85, 0x02, /* Report ID (2) */ + 0x09, 0x23, /* Usage (0x23) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x3E, /* Report Count (62) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0xC0, /* End Collection */ +}; + +#define PS5DS_OUTPUT_REPORT2_SIZE 63 + +struct ps5ds_out2 { + uint8_t valid_flag0; + uint8_t valid_flag1; + uint8_t rumble_right; + uint8_t rumble_left; + uint8_t reserved1[4]; + uint8_t mute_button_led; + uint8_t power_save_control; + uint8_t reserved2[28]; + uint8_t valid_flag2; + uint8_t reserved3[2]; + uint8_t lightbar_setup; + uint8_t led_brightness; + uint8_t player_leds; + uint8_t led_color_r; + uint8_t led_color_g; + uint8_t led_color_b; +} __attribute__((packed)); + +#define PS5DS_FLAG1_LIGHTBAR 0x04 +#define PS5DS_FLAG1_PLAYER_LEDS 0x10 +#define PS5DS_FLAG2_LIGHTBAR_SETUP 0x02 +#define PS5DS_LIGHTBAR_FADE_OUT 0x02 + +static const uint8_t ps5ds_player_leds[] = { + 0x04, /* Player 1: --x-- */ + 0x0A, /* Player 2: -x-x- */ + 0x15, /* Player 3: x-x-x */ + 0x1B, /* Player 4: xx-xx */ + 0x1F, /* Player 5: xxxxx */ +}; + +static const struct ps5ds_led { + int r; + int g; + int b; +} ps5ds_leds[] = { + /* The first 4 entries match the PS4, other from Linux driver */ + { 0x00, 0x00, 0x40 }, /* Blue */ + { 0x40, 0x00, 0x00 }, /* Red */ + { 0x00, 0x40, 0x00 }, /* Green */ + { 0x20, 0x00, 0x20 }, /* Pink */ + { 0x02, 0x01, 0x00 }, /* Orange */ + { 0x00, 0x01, 0x01 }, /* Teal */ + { 0x01, 0x01, 0x01 } /* White */ +}; + +enum ps5ds_led_state { + PS5DS_LED_OFF, + PS5DS_LED_ON, + PS5DS_LED_CNT, +}; + +struct ps5ds_softc { + struct hidmap hm; + + struct sx lock; + enum ps5ds_led_state led_state; + struct ps5ds_led led_color; + int player_leds; /* 0-4: player number, -1: off */ +}; + +#define PS5DS_OFFSET(field) offsetof(struct ps5ds_softc, field) +enum { + PS5DS_SYSCTL_LED_STATE = PS5DS_OFFSET(led_state), + PS5DS_SYSCTL_LED_COLOR_R = PS5DS_OFFSET(led_color.r), + PS5DS_SYSCTL_LED_COLOR_G = PS5DS_OFFSET(led_color.g), + PS5DS_SYSCTL_LED_COLOR_B = PS5DS_OFFSET(led_color.b), + PS5DS_SYSCTL_PLAYER_LEDS = PS5DS_OFFSET(player_leds), +#define PS5DS_SYSCTL_LAST PS5DS_SYSCTL_PLAYER_LEDS +}; + +#define PS5DS_MAP_BTN(number, code) \ + { HIDMAP_KEY(HUP_BUTTON, number, code) } +#define PS5DS_MAP_ABS(usage, code) \ + { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) } +#define PS5DS_MAP_FLT(usage, code) \ + { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), .flat = 15 } +#define PS5DS_MAP_GCB(usage, callback) \ + { HIDMAP_ANY_CB(HUP_GENERIC_DESKTOP, HUG_##usage, callback) } +#define PS5DS_FINALCB(cb) \ + { HIDMAP_FINAL_CB(&cb) } + +static hidmap_cb_t ps5ds_final_cb; + +static const struct hidmap_item ps5ds_map[] = { + PS5DS_MAP_FLT(X, ABS_X), + PS5DS_MAP_FLT(Y, ABS_Y), + PS5DS_MAP_ABS(Z, ABS_Z), + PS5DS_MAP_ABS(RZ, ABS_RZ), + PS5DS_MAP_FLT(RX, ABS_RX), + PS5DS_MAP_FLT(RY, ABS_RY), + PS5DS_MAP_BTN(1, BTN_WEST), + PS5DS_MAP_BTN(2, BTN_SOUTH), + PS5DS_MAP_BTN(3, BTN_EAST), + PS5DS_MAP_BTN(4, BTN_NORTH), + PS5DS_MAP_BTN(5, BTN_TL), + PS5DS_MAP_BTN(6, BTN_TR), + PS5DS_MAP_BTN(7, BTN_TL2), + PS5DS_MAP_BTN(8, BTN_TR2), + PS5DS_MAP_BTN(9, BTN_SELECT), + PS5DS_MAP_BTN(10, BTN_START), + PS5DS_MAP_BTN(11, BTN_THUMBL), + PS5DS_MAP_BTN(12, BTN_THUMBR), + PS5DS_MAP_BTN(13, BTN_MODE), + /* Click button is handled by touchpad driver */ + /* PS5DS_MAP_BTN(14, BTN_LEFT), */ + /* Linux mutes the microphone in hardware and emits no event */ + PS5DS_MAP_BTN(15, KEY_MICMUTE), + PS5DS_MAP_GCB(HAT_SWITCH, hgame_hat_switch_cb), + PS5DS_FINALCB( ps5ds_final_cb), +}; + +static const struct hid_device_id ps5ds_devs[] = { + { HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x0ce6), + HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD) }, +}; + +static int +ps5ds_final_cb(HIDMAP_CB_ARGS) +{ + struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); + + if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) { + evdev_support_prop(evdev, INPUT_PROP_DIRECT); + evdev_set_cdev_mode(evdev, UID_ROOT, GID_GAMES, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + } + + /* Do not execute callback at interrupt handler and detach */ + return (ENOSYS); +} + +static int +ps5ds_write(struct ps5ds_softc *sc) +{ + uint8_t buf[PS5DS_OUTPUT_REPORT2_SIZE]; + bool led_on; + + sx_assert(&sc->lock, SA_XLOCKED); + + memset(buf, 0, sizeof(buf)); + buf[0] = 0x02; + led_on = sc->led_state != PS5DS_LED_OFF; + *(struct ps5ds_out2 *)(buf + 1) = (struct ps5ds_out2) { + .valid_flag1 = PS5DS_FLAG1_LIGHTBAR | PS5DS_FLAG1_PLAYER_LEDS, + .player_leds = sc->player_leds < 0 ? + 0 : ps5ds_player_leds[sc->player_leds], + .led_color_r = led_on ? sc->led_color.r : 0, + .led_color_g = led_on ? sc->led_color.g : 0, + .led_color_b = led_on ? sc->led_color.b : 0, + }; + + return (hid_write(sc->hm.dev, buf, sizeof(buf))); +} + +static int +ps5ds_blank(struct ps5ds_softc *sc) +{ + uint8_t buf[PS5DS_OUTPUT_REPORT2_SIZE]; + + memset(buf, 0, sizeof(buf)); + buf[0] = 0x02; + *(struct ps5ds_out2 *)(buf + 1) = (struct ps5ds_out2) { + .valid_flag1 = PS5DS_FLAG1_LIGHTBAR | PS5DS_FLAG1_PLAYER_LEDS, + .valid_flag2 = PS5DS_FLAG2_LIGHTBAR_SETUP, + .lightbar_setup = PS5DS_LIGHTBAR_FADE_OUT, + }; + + return (hid_write(sc->hm.dev, buf, sizeof(buf))); +} + +static int +ps5ds_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct ps5ds_softc *sc; + int error, arg; + + if (oidp->oid_arg1 == NULL || oidp->oid_arg2 < 0 || + oidp->oid_arg2 > PS5DS_SYSCTL_LAST) + return (EINVAL); + + sc = oidp->oid_arg1; + sx_xlock(&sc->lock); + + arg = *(int *)((char *)sc + oidp->oid_arg2); + error = sysctl_handle_int(oidp, &arg, 0, req); + + if (error || !req->newptr) + goto unlock; + + switch (oidp->oid_arg2) { + case PS5DS_SYSCTL_LED_STATE: + if (arg < 0 || arg >= PS5DS_LED_CNT) + error = EINVAL; + break; + case PS5DS_SYSCTL_LED_COLOR_R: + case PS5DS_SYSCTL_LED_COLOR_G: + case PS5DS_SYSCTL_LED_COLOR_B: + if (arg < 0 || arg > UINT8_MAX) + error = EINVAL; + break; + case PS5DS_SYSCTL_PLAYER_LEDS: + if (arg < -1 || arg >= (int)nitems(ps5ds_player_leds)) + error = EINVAL; + break; + default: + error = EINVAL; + } + + if (error == 0) { + *(int *)((char *)sc + oidp->oid_arg2) = arg; + ps5ds_write(sc); + } +unlock: + sx_unlock(&sc->lock); + + return (error); +} + +static void +ps5ds_identify(driver_t *driver, device_t parent) +{ + + /* Replace the gamepad's rudimentary report descriptor */ + if (HIDBUS_LOOKUP_ID(parent, ps5ds_devs) != NULL) + hid_set_report_descr(parent, ps5ds_rdesc, + sizeof(ps5ds_rdesc)); +} + +static int +ps5ds_probe(device_t dev) +{ + struct ps5ds_softc *sc = device_get_softc(dev); + + hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR); + return ( + HIDMAP_PROBE(&sc->hm, dev, ps5ds_devs, ps5ds_map, NULL) + ); +} + +static int +ps5ds_attach(device_t dev) +{ + struct ps5ds_softc *sc = device_get_softc(dev); + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); + struct sysctl_oid *tree = device_get_sysctl_tree(dev); + int error, unit; + + sx_init(&sc->lock, "ps5dsense"); + + unit = device_get_unit(dev); + sc->led_state = PS5DS_LED_ON; + sc->led_color = ps5ds_leds[unit % nitems(ps5ds_leds)]; + sc->player_leds = unit < (int)nitems(ps5ds_player_leds) ? unit : -1; + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "led_state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc, + PS5DS_SYSCTL_LED_STATE, ps5ds_sysctl, "I", + "LED state: 0 - off, 1 - on."); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "led_color_r", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc, + PS5DS_SYSCTL_LED_COLOR_R, ps5ds_sysctl, "I", + "Lightbar color. Red component."); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "led_color_g", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc, + PS5DS_SYSCTL_LED_COLOR_G, ps5ds_sysctl, "I", + "Lightbar color. Green component."); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "led_color_b", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc, + PS5DS_SYSCTL_LED_COLOR_B, ps5ds_sysctl, "I", + "Lightbar color. Blue component."); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "player_leds", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc, + PS5DS_SYSCTL_PLAYER_LEDS, ps5ds_sysctl, "I", + "Player indicator LEDs: -1 - off, 0-4 - player number."); + + error = hidmap_attach(&sc->hm); + if (error) { + sx_destroy(&sc->lock); + return (error); + } + + sx_xlock(&sc->lock); + ps5ds_write(sc); + sx_xunlock(&sc->lock); + + return (0); +} + +static int +ps5ds_detach(device_t dev) +{ + struct ps5ds_softc *sc = device_get_softc(dev); + int error; + + error = hidmap_detach(&sc->hm); + if (error) + return (error); + + ps5ds_blank(sc); + sx_destroy(&sc->lock); + + return (0); +} + +static device_method_t ps5ds_methods[] = { + DEVMETHOD(device_identify, ps5ds_identify), + DEVMETHOD(device_probe, ps5ds_probe), + DEVMETHOD(device_attach, ps5ds_attach), + DEVMETHOD(device_detach, ps5ds_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ps5dsense, ps5ds_driver, ps5ds_methods, + sizeof(struct ps5ds_softc)); +DRIVER_MODULE(ps5dsense, hidbus, ps5ds_driver, NULL, NULL); + +MODULE_DEPEND(ps5dsense, hid, 1, 1, 1); +MODULE_DEPEND(ps5dsense, hidbus, 1, 1, 1); +MODULE_DEPEND(ps5dsense, hidmap, 1, 1, 1); +MODULE_DEPEND(ps5dsense, hgame, 1, 1, 1); +MODULE_DEPEND(ps5dsense, evdev, 1, 1, 1); +MODULE_VERSION(ps5dsense, 1); +HID_PNP_INFO(ps5ds_devs); diff --git a/sys/modules/hid/Makefile b/sys/modules/hid/Makefile index 100af866555a..ae4ad2c32133 100644 --- a/sys/modules/hid/Makefile +++ b/sys/modules/hid/Makefile @@ -20,6 +20,7 @@ SUBDIR += \ hsctrl \ ietp \ ps4dshock \ + ps5dsense \ u2f \ xb360gp diff --git a/sys/modules/hid/ps5dsense/Makefile b/sys/modules/hid/ps5dsense/Makefile new file mode 100644 index 000000000000..d9cf1e179602 --- /dev/null +++ b/sys/modules/hid/ps5dsense/Makefile @@ -0,0 +1,8 @@ +.PATH: ${SRCTOP}/sys/dev/hid + +KMOD= ps5dsense +SRCS= ps5dsense.c +SRCS+= opt_hid.h opt_kbd.h +SRCS+= bus_if.h device_if.h usbdevs.h + +.include