public inbox for linux-input@vger.kernel.org
 help / color / mirror / Atom feed
From: Damiano Gragnaniello <damianogragnaniello@gmail.com>
To: Jiri Kosina <jkosina@suse.cz>, Benjamin Tissoires <bentiss@kernel.org>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Damiano Gragnaniello <damianogragnaniello@gmail.com>,
	Jiri Kosina <jikos@kernel.org>,
	Benjamin Tissoires <benjamin.tissoires@redhat.com>
Subject: [PATCH] HID: magicmouse: add battery reporting for Magic Trackpad v1
Date: Wed, 15 Apr 2026 17:55:48 +0200	[thread overview]
Message-ID: <20260415155548.927385-1-damianogragnaniello@gmail.com> (raw)

The Magic Trackpad v1 (USB_DEVICE_ID_APPLE_MAGICTRACKPAD, 0x030e)
connects over Bluetooth and uses two AA batteries. When the device
sends Report ID 0x47, byte 1 carries the battery level as a
percentage (0-100) already expressed in firmware units.

This patch:
  - Registers a power_supply instance via devm_power_supply_register()
    during probe() for the v1 trackpad only (BT, vendor 0x05ac).
  - Parses Report ID 0x47 in magicmouse_raw_event() and calls
    power_supply_changed() to propagate the value to userspace.
  - Exposes PRESENT, CAPACITY, SCOPE and STATUS properties, consistent
    with how hid-sony and hid-logitech-hidpp expose battery data.
  - Registration failure is treated as non-fatal so the input device
    continues to work even if power_supply cannot be allocated.

Tested on Linux Mint / kernel 6.17 with an Apple Magic Trackpad A1339
(first generation, AA cells, firmware 0x0291).

Note: Technical analysis and initial boilerplate structure were assisted by 
Claude (Anthropic AI). The logic has been manually verified against hardware 
descriptors (rdesc) and tested on physical A1339 hardware.

Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Damiano Gragnaniello <damianogragnaniello@gmail.com>
---
--- /home/claude/hid-patch/hid-magicmouse.c.orig	2026-04-15 13:34:50.358612695 +0000
+++ /home/claude/hid-patch/hid-magicmouse.c	2026-04-15 13:35:39.402309524 +0000
@@ -15,6 +15,7 @@
 #include <linux/hid.h>
 #include <linux/input/mt.h>
 #include <linux/module.h>
+#include <linux/power_supply.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 
@@ -60,6 +61,7 @@
 #define MOUSE_REPORT_ID    0x29
 #define MOUSE2_REPORT_ID   0x12
 #define DOUBLE_REPORT_ID   0xf7
+#define TRACKPAD_V1_BATTERY_REPORT_ID 0x47
 #define USB_BATTERY_TIMEOUT_SEC 60
 
 /* These definitions are not precise, but they're close enough.  (Bits
@@ -124,6 +126,10 @@
  * @hdev: Pointer to the underlying HID device.
  * @work: Workqueue to handle initialization retry for quirky devices.
  * @battery_timer: Timer for obtaining battery level information.
+ * @battery: Power supply instance for Magic Trackpad v1 AA battery reporting.
+ * @battery_desc: Descriptor for the power_supply registration.
+ * @battery_name: Name buffer for the power_supply instance.
+ * @battery_capacity: Last known battery level (0-100%) for Magic Trackpad v1.
  */
 struct magicmouse_sc {
 	struct input_dev *input;
@@ -149,8 +155,46 @@
 	struct hid_device *hdev;
 	struct delayed_work work;
 	struct timer_list battery_timer;
+
+	/* Magic Trackpad v1 (AA battery) power_supply support */
+	struct power_supply		*battery;
+	struct power_supply_desc	 battery_desc;
+	char				 battery_name[64];
+	int				 battery_capacity;
+};
+
+static const enum power_supply_property magicmouse_v1_battery_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+	POWER_SUPPLY_PROP_STATUS,
 };
 
+static int magicmouse_v1_battery_get_property(struct power_supply *psy,
+					       enum power_supply_property psp,
+					       union power_supply_propval *val)
+{
+	struct magicmouse_sc *msc = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = msc->battery_capacity;
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int magicmouse_firm_touch(struct magicmouse_sc *msc)
 {
 	int touch = -1;
@@ -391,6 +435,19 @@
 	int x = 0, y = 0, ii, clicks = 0, npoints;
 
 	switch (data[0]) {
+	case TRACKPAD_V1_BATTERY_REPORT_ID:
+		/*
+		 * Magic Trackpad v1 (AA battery, 0x030e) sends battery level
+		 * in byte 1, already expressed as a percentage (0-100).
+		 * Clamp defensively and notify the power_supply framework.
+		 */
+		if (size < 2)
+			return 0;
+		if (msc->battery) {
+			msc->battery_capacity = clamp_val((int)data[1], 0, 100);
+			power_supply_changed(msc->battery);
+		}
+		return 0;
 	case TRACKPAD_REPORT_ID:
 	case TRACKPAD2_BT_REPORT_ID:
 		/* Expect four bytes of prefix, and N*9 bytes of touch data. */
@@ -890,6 +947,38 @@
 		magicmouse_fetch_battery(hdev);
 	}
 
+	/* Register power_supply for Magic Trackpad v1 (AA battery, BT only) */
+	if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD &&
+	    id->vendor == USB_VENDOR_ID_APPLE) {
+		struct power_supply_config psy_cfg = {};
+
+		msc->battery_capacity = 0;
+		snprintf(msc->battery_name, sizeof(msc->battery_name),
+			 "hid-magictrackpad-v1-%s", dev_name(&hdev->dev));
+
+		msc->battery_desc.name           = msc->battery_name;
+		msc->battery_desc.type           = POWER_SUPPLY_TYPE_BATTERY;
+		msc->battery_desc.properties     = magicmouse_v1_battery_props;
+		msc->battery_desc.num_properties =
+			ARRAY_SIZE(magicmouse_v1_battery_props);
+		msc->battery_desc.get_property   =
+			magicmouse_v1_battery_get_property;
+
+		psy_cfg.drv_data = msc;
+
+		msc->battery = devm_power_supply_register(&hdev->dev,
+							  &msc->battery_desc,
+							  &psy_cfg);
+		if (IS_ERR(msc->battery)) {
+			ret = PTR_ERR(msc->battery);
+			hid_err(hdev,
+				"unable to register trackpad v1 battery: %d\n",
+				ret);
+			msc->battery = NULL;
+			/* Non-fatal: continue without battery reporting */
+		}
+	}
+
 	if (is_usb_magicmouse2(id->vendor, id->product) ||
 	    (is_usb_magictrackpad2(id->vendor, id->product) &&
 	     hdev->type != HID_TYPE_USBMOUSE))

             reply	other threads:[~2026-04-15 15:56 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-15 15:55 Damiano Gragnaniello [this message]
2026-04-15 20:41 ` [PATCH v2] HID: magicmouse: add battery reporting for Magic Trackpad v1 Damiano Gragnaniello
2026-04-15 21:31 ` [PATCH v3] " Damiano Gragnaniello
2026-04-15 21:53 ` [PATCH v4] " Damiano Gragnaniello
2026-04-16 12:53 ` [PATCH v5] " Damiano Gragnaniello
2026-04-16 14:33 ` [PATCH v6] " Damiano Gragnaniello

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260415155548.927385-1-damianogragnaniello@gmail.com \
    --to=damianogragnaniello@gmail.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=bentiss@kernel.org \
    --cc=jikos@kernel.org \
    --cc=jkosina@suse.cz \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox