一定会写的。。。//2024061 简介之前留言了一定会写所以还是抽时间把这个弄了。因为这次用的高通的板子IO输出是1.8V无法驱动一个真实的LED所以用的迷你示波器看波形。然后这次用的版本是userdebug所以自启动selinux这些也都没有弄算是简化版的简化版。2 DriverDTS/* user-led bring-up patch (temporary; see neo-aliso-sg2-idp.user-led.patch.dtsi) */ /* ---- 2) Custom user LED platform device on GPIO 19 ---- */ tlmm { user_led_default: user-led-default { mux { pins gpio19; function gpio; }; config { pins gpio19; drive-strength 2; bias-disable; output-low; }; }; }; soc { user_led: user-led { compatible vendor,user-led; label user_led; led-gpios tlmm 19 0; /* GPIO 19, active high */ pinctrl-names default; pinctrl-0 user_led_default; status okay; }; };代码// SPDX-License-Identifier: GPL-2.0-only /* * user_led_driver.c - Custom platform driver to control a user LED via GPIO * * Exposes a sysfs brightness attribute under its platform device: * echo 1 .../user-led/brightness (turn LED on) * echo 0 .../user-led/brightness (turn LED off) * * Device tree node expected (see user-led.dtsi): * user_led { * compatible vendor,user-led; * led-gpios tlmm 19 0; // GPIO 19 (placeholder, per user) * label user_led; * }; * * Build: KLEAF ddk_module (see BUILD.bazel) */ #include linux/module.h #include linux/platform_device.h #include linux/of.h #include linux/gpio/consumer.h #include linux/sysfs.h #include linux/device.h #include linux/err.h #include linux/mutex.h #include linux/slab.h #define DRIVER_NAME user-led #define DRIVER_DESC User LED GPIO platform driver struct user_led_dev { struct device *dev; struct gpio_desc *led_gpio; struct mutex lock; int brightness; }; /* -------------------- sysfs attribute -------------------- */ static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct user_led_dev *led dev_get_drvdata(dev); return sysfs_emit(buf, %d\n, led-brightness); } static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct user_led_dev *led dev_get_drvdata(dev); unsigned long value; int ret; ret kstrtoul(buf, 0, value); if (ret) return ret; if (value 1) return -EINVAL; mutex_lock(led-lock); gpiod_set_value_cansleep(led-led_gpio, value); led-brightness value; mutex_unlock(led-lock); return count; } static DEVICE_ATTR_RW(brightness); static struct attribute *user_led_attrs[] { dev_attr_brightness.attr, NULL, }; static struct attribute_group user_led_group { .attrs user_led_attrs, }; /* -------------------- platform driver -------------------- */ static int user_led_probe(struct platform_device *pdev) { struct device *dev pdev-dev; struct user_led_dev *led; int ret; led devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led-dev dev; mutex_init(led-lock); dev_set_drvdata(dev, led); /* * GPIO number comes from DTS led-gpios (currently tlmm 19 0). * The flags (0 active high, GPIO_ACTIVE_LOW active low) are taken * from DTS too, so the driver works for either polarity. */ led-led_gpio devm_gpiod_get(dev, led, GPIOD_OUT_LOW); if (IS_ERR(led-led_gpio)) { ret PTR_ERR(led-led_gpio); dev_err(dev, failed to get led-gpios: %d\n, ret); return ret; } gpiod_set_consumer_name(led-led_gpio, user-led); led-brightness 0; /* Create the sysfs brightness attribute under the platform device. */ ret devm_device_add_group(dev, user_led_group); if (ret) { dev_err(dev, failed to create sysfs group: %d\n, ret); return ret; } dev_info(dev, user LED driver probed, GPIO active-%s\n, gpiod_is_active_low(led-led_gpio) ? low : high); return 0; } static int user_led_remove(struct platform_device *pdev) { struct user_led_dev *led dev_get_drvdata(pdev-dev); if (led-led_gpio) gpiod_set_value_cansleep(led-led_gpio, 0); return 0; } static const struct of_device_id user_led_of_match[] { { .compatible vendor,user-led }, { } }; MODULE_DEVICE_TABLE(of, user_led_of_match); static struct platform_driver user_led_driver { .probe user_led_probe, .remove user_led_remove, .driver { .name DRIVER_NAME, .of_match_table user_led_of_match, }, }; module_platform_driver(user_led_driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(Your Name); MODULE_DESCRIPTION(DRIVER_DESC);此时可以先在命令行下面观察执行结果3 App核心就是控制那个节点。package com.example.userled import android.os.Bundle import android.widget.Button import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File /** * Control App for the user LED. * * Writes 1/0 to the driver sysfs brightness node via root (su): * /sys/devices/.../user-led/brightness * * NOTE: The exact sysfs path depends on the platform device naming. * On Qualcomm platforms the node usually appears under /sys/devices/platform/ * or /sys/bus/platform/devices/. A robust way is to find it by reading the * label file, or use the fixed path if known. We locate it dynamically: * find /sys/devices -name brightness -path *user-led* */ class MainActivity : AppCompatActivity() { private val ledBrightnessPath /sys/devices/platform/soc/soc:user-led/brightness private lateinit var statusText: TextView private lateinit var btnOn: Button private lateinit var btnOff: Button private lateinit var btnFind: Button private val scope CoroutineScope(Dispatchers.Main) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) statusText findViewById(R.id.statusText) btnOn findViewById(R.id.btnOn) btnOff findViewById(R.id.btnOff) btnFind findViewById(R.id.btnFind) btnOn.setOnClickListener { setLed(1) } btnOff.setOnClickListener { setLed(0) } btnFind.setOnClickListener { findLedPath() } } override fun onDestroy() { super.onDestroy() RootHelper.release() } private fun setLed(value: Int) { scope.launch { val result withContext(Dispatchers.IO) { writeLed(value) } val (ok, message) result if (ok) { statusText.text LED ${if (value 1) ON else OFF} $ledBrightnessPath } else { statusText.text Failed: $message toast(message) } } } private fun findLedPath() { scope.launch { val path withContext(Dispatchers.IO) { locateLedBrightness() } if (path ! null) { statusText.text Found: $path toast(Found: $path) } else { statusText.text Not found (driver not loaded / not probed) toast(LED sysfs node not found) } } } /** Locates the brightness node for the user-led device. */ private fun locateLedBrightness(): String? { val node File(ledBrightnessPath) return ledBrightnessPath.takeIf { node.exists() } } private fun writeLed(value: Int): PairBoolean, String { val node File(ledBrightnessPath) if (!node.exists()) return false to LED driver node not found if (!node.canWrite()) return false to LED node is not writable return try { node.writeText(value.toString()) true to ledBrightnessPath } catch (e: Exception) { false to Write failed: ${e.message ?: e.javaClass.simpleName} } } private fun toast(msg: String) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() } }UI