diff mbox series

HID: playstation: DS4: Add BT poll interval adjustment

Message ID 20250508214305.836406-1-titanv3585@gmail.com
State New
Headers show
Series HID: playstation: DS4: Add BT poll interval adjustment | expand

Commit Message

Vadym Tytan May 8, 2025, 9:41 p.m. UTC
Between v6.1 and v6.2 versions of Linux kernel,
DualShock4 related code was moved from `hid-sony.c`
to `hid-playstation.c` (near DualSense code) and
Bluetooth poll interval adjustment functionality was lost

Signed-off-by: Vadym Tytan <titanv3585@gmail.com>
---
 CREDITS                       |  4 +++
 drivers/hid/hid-playstation.c | 64 +++++++++++++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 3 deletions(-)

Comments

kernel test robot May 9, 2025, 10:06 p.m. UTC | #1
Hi Vadym,

kernel test robot noticed the following build warnings:

[auto build test WARNING on hid/for-next]
[also build test WARNING on linus/master v6.15-rc5 next-20250509]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadym-Tytan/HID-playstation-DS4-Add-BT-poll-interval-adjustment/20250509-054413
base:   https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git for-next
patch link:    https://lore.kernel.org/r/20250508214305.836406-1-titanv3585%40gmail.com
patch subject: [PATCH] HID: playstation: DS4: Add BT poll interval adjustment
config: x86_64-randconfig-122-20250509 (https://download.01.org/0day-ci/archive/20250510/202505100535.vKH3zHW6-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250510/202505100535.vKH3zHW6-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505100535.vKH3zHW6-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/hid/hid-playstation.c:1773:25: sparse: sparse: symbol 'dev_attr_dualshock4_bt_poll_interval' was not declared. Should it be static?

vim +/dev_attr_dualshock4_bt_poll_interval +1773 drivers/hid/hid-playstation.c

  1772	
> 1773	struct device_attribute dev_attr_dualshock4_bt_poll_interval = {
  1774		.attr	= { .name = "bt_poll_interval", .mode = 0644 },
  1775		.show	= dualshock4_show_poll_interval,
  1776		.store	= dualshock4_store_poll_interval,
  1777	};
  1778
Vadym Tytan May 13, 2025, 8:44 a.m. UTC | #2
> kernel test robot noticed the following build warnings:
>
> sparse warnings: (new ones prefixed by >>)
>>> drivers/hid/hid-playstation.c:1773:25: sparse: sparse: symbol 'dev_attr_dualshock4_bt_poll_interval' was not declared. Should it be static?
> 
> vim +/dev_attr_dualshock4_bt_poll_interval +1773 drivers/hid/hid-playstation.c
> 
>    1772	
>> 1773	struct device_attribute dev_attr_dualshock4_bt_poll_interval = {
>    1774		.attr	= { .name = "bt_poll_interval", .mode = 0644 },
>    1775		.show	= dualshock4_show_poll_interval,
>    1776		.store	= dualshock4_store_poll_interval,
>    1777	};
>    1778	

This piece of code is expanded `DEVICE_ATTR` macro with changed variable 
name.
`DEVICE_ATTR` is defined as:
```c
// Reference: 
https://github.com/intel-lab-lkp/linux/blob/Vadym-Tytan/HID-playstation-DS4-Add-BT-poll-interval-adjustment/20250509-054413/include/linux/device.h#L138-L158
#define DEVICE_ATTR(_name, _mode, _show, _store) \
	struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, 
_store)
```
It doesn't use `static` so neither did I.

P.S. Macro was expanded and variable name was changed from 
`dev_attr_bt_poll_interval` to `dev_attr_dualshock4_bt_poll_interval` 
because this attribute only applies to DualShock4
Roderick Colenbrander June 8, 2025, 6:08 p.m. UTC | #3
Hi,

Sorry for the late reply (business travel / vacation). This is the
only code I left out during hid-playstation creation. At the time when
I added this to hid-sony it felt like a good idea. But we later found
out the console wasn't even using this code itself. We haven't had a
need for it in various embedded projects. The code had significant
impact on battery (especially when set to like 1ms). The timing itself
was not that stable, it made it better from the hardware side
(measured using a BT packet sniffer), but there is overhead in
bluetooth stacks, which makes the timing not that predictable

Do you have a strong use case or reason to add it back?

Thanks,
Roderick

On Thu, May 8, 2025 at 2:43 PM Vadym Tytan <titanv3585@gmail.com> wrote:
>
> Between v6.1 and v6.2 versions of Linux kernel,
> DualShock4 related code was moved from `hid-sony.c`
> to `hid-playstation.c` (near DualSense code) and
> Bluetooth poll interval adjustment functionality was lost
>
> Signed-off-by: Vadym Tytan <titanv3585@gmail.com>
> ---
>  CREDITS                       |  4 +++
>  drivers/hid/hid-playstation.c | 64 +++++++++++++++++++++++++++++++++--
>  2 files changed, 65 insertions(+), 3 deletions(-)
>
> diff --git a/CREDITS b/CREDITS
> index f74d230992d6..137c3636e318 100644
> --- a/CREDITS
> +++ b/CREDITS
> @@ -4001,6 +4001,10 @@ S: 44 Campbell Park Crescent
>  S: Edinburgh EH13 0HT
>  S: United Kingdom
>
> +N: Vadym Tytan
> +E: titanv3585@gmail.com
> +D: Minor game controllers features
> +
>  N: Thomas Uhl
>  E: uhl@sun1.rz.fh-heilbronn.de
>  D: Application programmer
> diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
> index 1468fb11e39d..fd51f00b0516 100644
> --- a/drivers/hid/hid-playstation.c
> +++ b/drivers/hid/hid-playstation.c
> @@ -337,7 +337,7 @@ struct dualsense_output_report {
>   * 0x3F - disabled
>   */
>  #define DS4_OUTPUT_HWCTL_BT_POLL_MASK  0x3F
> -/* Default to 4ms poll interval, which is same as USB (not adjustable). */
> +/* Default to 4ms poll interval, which is same as USB (adjustable). */

The not adjustable referred to USB, which is not adjustable. So keep
that one the way it is.

>  #define DS4_BT_DEFAULT_POLL_INTERVAL_MS        4
>  #define DS4_OUTPUT_HWCTL_CRC32         0x40
>  #define DS4_OUTPUT_HWCTL_HID           0x80
> @@ -542,6 +542,7 @@ static inline void dualsense_schedule_work(struct dualsense *ds);
>  static inline void dualshock4_schedule_work(struct dualshock4 *ds4);
>  static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue);
>  static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4);
> +static int dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval);
>
>  /*
>   * Add a new ps_device to ps_devices if it doesn't exist.
> @@ -1738,6 +1739,43 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
>         return ERR_PTR(ret);
>  }
>
> +static ssize_t dualshock4_show_poll_interval(struct device *dev,
> +                               struct device_attribute
> +                               *attr, char *buf)
> +{
> +       struct hid_device *hdev = to_hid_device(dev);
> +       struct ps_device *ps_dev = hid_get_drvdata(hdev);
> +       struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
> +
> +       return sysfs_emit(buf, "%i\n", ds4->bt_poll_interval);
> +}
> +
> +static ssize_t dualshock4_store_poll_interval(struct device *dev,
> +                               struct device_attribute *attr,
> +                               const char *buf, size_t count)
> +{
> +       struct hid_device *hdev = to_hid_device(dev);
> +       struct ps_device *ps_dev = hid_get_drvdata(hdev);
> +       struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
> +       int ret;
> +       u8 interval;
> +
> +       if (kstrtou8(buf, 0, &interval))
> +               return -EINVAL;
> +
> +       ret = dualshock4_set_bt_poll_interval(ds4, interval);
> +       if (ret)
> +               return ret;
> +
> +       return count;
> +}
> +
> +struct device_attribute dev_attr_dualshock4_bt_poll_interval = {
> +       .attr   = { .name = "bt_poll_interval", .mode = 0644 },
> +       .show   = dualshock4_show_poll_interval,
> +       .store  = dualshock4_store_poll_interval,
> +};
> +
>  static void dualshock4_dongle_calibration_work(struct work_struct *work)
>  {
>         struct dualshock4 *ds4 = container_of(work, struct dualshock4, dongle_hotplug_worker);
> @@ -2493,6 +2531,9 @@ static void dualshock4_remove(struct ps_device *ps_dev)
>         struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
>         unsigned long flags;
>
> +       if (ps_dev->hdev->bus == BUS_BLUETOOTH)
> +               device_remove_file(&ps_dev->hdev->dev, &dev_attr_dualshock4_bt_poll_interval);
> +
>         spin_lock_irqsave(&ds4->base.lock, flags);
>         ds4->output_worker_initialized = false;
>         spin_unlock_irqrestore(&ds4->base.lock, flags);
> @@ -2513,11 +2554,16 @@ static inline void dualshock4_schedule_work(struct dualshock4 *ds4)
>         spin_unlock_irqrestore(&ds4->base.lock, flags);
>  }
>
> -static void dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval)
> +static int dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval)
>  {
> +       if (interval >= DS4_OUTPUT_HWCTL_BT_POLL_MASK)
> +               return -EINVAL;
> +
>         ds4->bt_poll_interval = interval;
>         ds4->update_bt_poll_interval = true;
>         dualshock4_schedule_work(ds4);
> +
> +       return 0;
>  }
>
>  /* Set default lightbar color based on player. */
> @@ -2659,7 +2705,17 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
>                         goto err;
>         }
>
> -       dualshock4_set_bt_poll_interval(ds4, DS4_BT_DEFAULT_POLL_INTERVAL_MS);
> +       if (hdev->bus == BUS_BLUETOOTH) {
> +               ret = dualshock4_set_bt_poll_interval(ds4, DS4_BT_DEFAULT_POLL_INTERVAL_MS);
> +               if (ret) {
> +                       hid_err(hdev, "Failed to set poll interval for DualShock4: %d\n", ret);
> +                       goto err;
> +               }
> +
> +               ret = device_create_file(&hdev->dev, &dev_attr_dualshock4_bt_poll_interval);
> +               if (ret)
> +                       hid_warn(hdev, "can't create sysfs bt_poll_interval attribute err: %d\n", ret);
> +       }
>
>         ret = ps_device_set_player_id(ps_dev);
>         if (ret) {
> @@ -2678,6 +2734,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
>         return &ds4->base;
>
>  err:
> +       if (hdev->bus == BUS_BLUETOOTH)
> +               device_remove_file(&hdev->dev, &dev_attr_dualshock4_bt_poll_interval);
>         ps_devices_list_remove(ps_dev);
>         return ERR_PTR(ret);
>  }
> --
> 2.49.0
>
>
Vadym Tytan June 9, 2025, 12:25 p.m. UTC | #4
> Do you have a strong use case or reason to add it back?

I wouldn't say *strong* per se, but if something:
- Can be adjustable
   - I think if hardware is capable of such thing it's better to have 
adjustment of it available to user.
   - It's better to have an option then no option at all, let the user 
decide.
- Doesn't break the hardware nor software
   - When decreasing polling interval
     - Battery impact is just faster discharging, it doesn't "breaks" it
     - Bluetooth stack won't break, it could be just "overwhelmed"
   - When increasing polling interval
     - No negative impact at all (if we wouldn't count the user who can 
set polling interval to like 63, and have choppy controls, that's why 
defaults exists)
- Have benefits
   - PI can be decreased for smoother controls in cost of battery life
   - PI can be also increased to save battery in cost of smooth controls
- Doesn't impact anything nor anybody on defaults.

> The not adjustable referred to USB, which is not adjustable. So keep
> that one the way it is.

Oh, OK, I understood this in another way, but that make sense too.
diff mbox series

Patch

diff --git a/CREDITS b/CREDITS
index f74d230992d6..137c3636e318 100644
--- a/CREDITS
+++ b/CREDITS
@@ -4001,6 +4001,10 @@  S: 44 Campbell Park Crescent
 S: Edinburgh EH13 0HT
 S: United Kingdom
 
+N: Vadym Tytan
+E: titanv3585@gmail.com
+D: Minor game controllers features
+
 N: Thomas Uhl
 E: uhl@sun1.rz.fh-heilbronn.de
 D: Application programmer
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 1468fb11e39d..fd51f00b0516 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -337,7 +337,7 @@  struct dualsense_output_report {
  * 0x3F - disabled
  */
 #define DS4_OUTPUT_HWCTL_BT_POLL_MASK	0x3F
-/* Default to 4ms poll interval, which is same as USB (not adjustable). */
+/* Default to 4ms poll interval, which is same as USB (adjustable). */
 #define DS4_BT_DEFAULT_POLL_INTERVAL_MS	4
 #define DS4_OUTPUT_HWCTL_CRC32		0x40
 #define DS4_OUTPUT_HWCTL_HID		0x80
@@ -542,6 +542,7 @@  static inline void dualsense_schedule_work(struct dualsense *ds);
 static inline void dualshock4_schedule_work(struct dualshock4 *ds4);
 static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue);
 static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4);
+static int dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval);
 
 /*
  * Add a new ps_device to ps_devices if it doesn't exist.
@@ -1738,6 +1739,43 @@  static struct ps_device *dualsense_create(struct hid_device *hdev)
 	return ERR_PTR(ret);
 }
 
+static ssize_t dualshock4_show_poll_interval(struct device *dev,
+				struct device_attribute
+				*attr, char *buf)
+{
+	struct hid_device *hdev = to_hid_device(dev);
+	struct ps_device *ps_dev = hid_get_drvdata(hdev);
+	struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
+
+	return sysfs_emit(buf, "%i\n", ds4->bt_poll_interval);
+}
+
+static ssize_t dualshock4_store_poll_interval(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct hid_device *hdev = to_hid_device(dev);
+	struct ps_device *ps_dev = hid_get_drvdata(hdev);
+	struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
+	int ret;
+	u8 interval;
+
+	if (kstrtou8(buf, 0, &interval))
+		return -EINVAL;
+
+	ret = dualshock4_set_bt_poll_interval(ds4, interval);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+struct device_attribute dev_attr_dualshock4_bt_poll_interval = {
+	.attr	= { .name = "bt_poll_interval", .mode = 0644 },
+	.show	= dualshock4_show_poll_interval,
+	.store	= dualshock4_store_poll_interval,
+};
+
 static void dualshock4_dongle_calibration_work(struct work_struct *work)
 {
 	struct dualshock4 *ds4 = container_of(work, struct dualshock4, dongle_hotplug_worker);
@@ -2493,6 +2531,9 @@  static void dualshock4_remove(struct ps_device *ps_dev)
 	struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
 	unsigned long flags;
 
+	if (ps_dev->hdev->bus == BUS_BLUETOOTH)
+		device_remove_file(&ps_dev->hdev->dev, &dev_attr_dualshock4_bt_poll_interval);
+
 	spin_lock_irqsave(&ds4->base.lock, flags);
 	ds4->output_worker_initialized = false;
 	spin_unlock_irqrestore(&ds4->base.lock, flags);
@@ -2513,11 +2554,16 @@  static inline void dualshock4_schedule_work(struct dualshock4 *ds4)
 	spin_unlock_irqrestore(&ds4->base.lock, flags);
 }
 
-static void dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval)
+static int dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval)
 {
+	if (interval >= DS4_OUTPUT_HWCTL_BT_POLL_MASK)
+		return -EINVAL;
+
 	ds4->bt_poll_interval = interval;
 	ds4->update_bt_poll_interval = true;
 	dualshock4_schedule_work(ds4);
+
+	return 0;
 }
 
 /* Set default lightbar color based on player. */
@@ -2659,7 +2705,17 @@  static struct ps_device *dualshock4_create(struct hid_device *hdev)
 			goto err;
 	}
 
-	dualshock4_set_bt_poll_interval(ds4, DS4_BT_DEFAULT_POLL_INTERVAL_MS);
+	if (hdev->bus == BUS_BLUETOOTH) {
+		ret = dualshock4_set_bt_poll_interval(ds4, DS4_BT_DEFAULT_POLL_INTERVAL_MS);
+		if (ret) {
+			hid_err(hdev, "Failed to set poll interval for DualShock4: %d\n", ret);
+			goto err;
+		}
+
+		ret = device_create_file(&hdev->dev, &dev_attr_dualshock4_bt_poll_interval);
+		if (ret)
+			hid_warn(hdev, "can't create sysfs bt_poll_interval attribute err: %d\n", ret);
+	}
 
 	ret = ps_device_set_player_id(ps_dev);
 	if (ret) {
@@ -2678,6 +2734,8 @@  static struct ps_device *dualshock4_create(struct hid_device *hdev)
 	return &ds4->base;
 
 err:
+	if (hdev->bus == BUS_BLUETOOTH)
+		device_remove_file(&hdev->dev, &dev_attr_dualshock4_bt_poll_interval);
 	ps_devices_list_remove(ps_dev);
 	return ERR_PTR(ret);
 }