[PATCH 5/6] platform/x86: ideapad-laptop: Expose camera_power only if supported

From: Eray Orçunus
Date: Wed Oct 26 2022 - 15:03:43 EST


IdeaPads dropped support for VPCCMD_W_CAMERA somewhere between 2014-2016,
none of the IdeaPads produced after that I tested supports it. Fortunately
I found a way to check it; if the DSDT has camera device(s) defined, it
shouldn't have working VPCCMD_W_CAMERA, thus camera_power shouldn't be
exposed to sysfs. To accomplish this, walk the ACPI namespace in
ideapad_check_features and check the devices starting with "CAM".
Tested on 520-15IKB and Legion Y520, which successfully didn't expose
the camera_power attribute.

Link: https://www.spinics.net/lists/platform-driver-x86/msg26147.html
Signed-off-by: Eray Orçunus <erayorcunus@xxxxxxxxx>
---
drivers/platform/x86/ideapad-laptop.c | 53 ++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index f3d4f2beda07..65eea2e65bbe 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -149,6 +149,7 @@ struct ideapad_private {
bool fn_lock : 1;
bool hw_rfkill_switch : 1;
bool kbd_bl : 1;
+ bool cam_ctrl_via_ec : 1;
bool touchpad_ctrl_via_ec : 1;
bool usb_charging : 1;
} features;
@@ -163,6 +164,26 @@ static bool no_bt_rfkill;
module_param(no_bt_rfkill, bool, 0444);
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");

+static char *cam_device_prefix = "CAM";
+
+static acpi_status acpi_find_device_callback(acpi_handle handle, u32 level,
+ void *context, void **return_value)
+{
+ char buffer[8];
+ struct acpi_buffer ret_buf;
+
+ ret_buf.length = sizeof(buffer);
+ ret_buf.pointer = buffer;
+
+ if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &ret_buf)))
+ if (strncmp(ret_buf.pointer, context, strlen(context)) == 0) {
+ *return_value = handle;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
/*
* ACPI Helpers
*/
@@ -675,7 +696,7 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
bool supported = true;

if (attr == &dev_attr_camera_power.attr)
- supported = test_bit(CFG_CAP_CAM_BIT, &priv->cfg);
+ supported = priv->features.cam_ctrl_via_ec;
else if (attr == &dev_attr_conservation_mode.attr)
supported = priv->features.conservation_mode;
else if (attr == &dev_attr_fan_mode.attr)
@@ -1523,10 +1544,40 @@ static const struct dmi_system_id hw_rfkill_list[] = {
static void ideapad_check_features(struct ideapad_private *priv)
{
acpi_handle handle = priv->adev->handle;
+ acpi_handle pci_handle;
+ acpi_handle temp_handle = NULL;
unsigned long val;
+ acpi_status status;

priv->features.hw_rfkill_switch = dmi_check_system(hw_rfkill_list);

+ /*
+ * Some IdeaPads have camera switch via EC (mostly older ones),
+ * some don't. Fortunately we know that if DSDT contains device
+ * object for the camera, camera isn't switchable via EC.
+ * So, let's walk the namespace and try to find CAM* object.
+ * If we can't find it, set cam_ctrl_via_ec to true.
+ */
+
+ priv->features.cam_ctrl_via_ec = false;
+
+ if (test_bit(CFG_CAP_CAM_BIT, &priv->cfg)) {
+ status = acpi_get_handle(handle, "^^^", &pci_handle);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, pci_handle,
+ ACPI_UINT32_MAX,
+ acpi_find_device_callback,
+ NULL, cam_device_prefix,
+ &temp_handle);
+
+ if (ACPI_SUCCESS(status) && temp_handle == NULL)
+ priv->features.cam_ctrl_via_ec = true;
+
+ } else
+ dev_warn(&priv->platform_device->dev,
+ "Could not find PCI* node in the namespace\n");
+ }
+
/* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */
priv->features.touchpad_ctrl_via_ec = !acpi_dev_present("ELAN0634", NULL, -1);

--
2.34.1