lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed,  9 Sep 2020 18:25:50 +0200
From:   Marek Behún <marek.behun@....cz>
To:     netdev@...r.kernel.org
Cc:     linux-leds@...r.kernel.org, Pavel Machek <pavel@....cz>,
        Dan Murphy <dmurphy@...com>,
        Ondřej Jirman <megous@...ous.com>,
        Russell King <linux@...linux.org.uk>,
        Andrew Lunn <andrew@...n.ch>, linux-kernel@...r.kernel.org,
        Matthias Schiffer <matthias.schiffer@...tq-group.com>,
        "David S. Miller" <davem@...emloft.net>,
        Marek Behún <marek.behun@....cz>
Subject: [PATCH net-next + leds v2 5/7] net: phy: add support for LEDs controlled by ethernet PHY chips

This patch uses the new API for HW controlled LEDs to add support for
probing and control of LEDs connected to an ethernet PHY chip.

A PHY driver wishing to utilize this API needs to implement the methods
in struct hw_controlled_led_ops and set the member led_ops in struct
phy_driver to point to that structure.

Signed-off-by: Marek Behún <marek.behun@....cz>
---
 drivers/net/phy/phy_device.c | 103 +++++++++++++++++++++++++++++++++++
 include/linux/phy.h          |   4 ++
 2 files changed, 107 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 38f56d39f1229..54d5c88e4d4b2 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2820,6 +2820,103 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv)
 	return phydrv->config_intr && phydrv->ack_interrupt;
 }
 
+#if IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED)
+
+/* PHY mutex lock wrappers for operations on PHY HW controlled LEDs, so that PHY drivers
+ * implementing these operations don't have to lock phydev->lock themselves.
+ */
+static int phy_led_init(struct device *dev, struct hw_controlled_led *led)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	int ret;
+
+	mutex_lock(&phydev->lock);
+	ret = phydev->drv->led_ops->led_init(dev, led);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+
+static int phy_led_brightness_set(struct device *dev, struct hw_controlled_led *led,
+				  enum led_brightness brightness)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	int ret;
+
+	mutex_lock(&phydev->lock);
+	ret = phydev->drv->led_ops->led_brightness_set(dev, led, brightness);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+
+static const char *phy_led_iter_hw_mode(struct device *dev, struct hw_controlled_led *led,
+					void **iter)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	const char *ret;
+
+	mutex_lock(&phydev->lock);
+	ret = phydev->drv->led_ops->led_iter_hw_mode(dev, led, iter);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+
+static int phy_led_set_hw_mode(struct device *dev, struct hw_controlled_led *led, const char *mode)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	int ret;
+
+	mutex_lock(&phydev->lock);
+	ret = phydev->drv->led_ops->led_set_hw_mode(dev, led, mode);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+
+static const char *phy_led_get_hw_mode(struct device *dev, struct hw_controlled_led *led)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	const char *ret;
+
+	mutex_lock(&phydev->lock);
+	ret = phydev->drv->led_ops->led_get_hw_mode(dev, led);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+
+static const struct hw_controlled_led_ops phy_hw_controlled_led_ops = {
+	.led_init		= phy_led_init,
+	.led_brightness_set	= phy_led_brightness_set,
+	.led_iter_hw_mode	= phy_led_iter_hw_mode,
+	.led_set_hw_mode	= phy_led_set_hw_mode,
+	.led_get_hw_mode	= phy_led_get_hw_mode,
+};
+
+static int of_phy_probe_leds(struct phy_device *phydev)
+{
+	char devicename[32];
+
+	if (!phydev->drv->led_ops)
+		return 0;
+
+	snprintf(devicename, sizeof(devicename), "ethernet-phy%i", phydev->phyindex);
+
+	return of_register_hw_controlled_leds(&phydev->mdio.dev, devicename,
+					      &phy_hw_controlled_led_ops);
+}
+
+#else /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */
+
+static inline int of_phy_probe_leds(struct phy_device *phydev)
+{
+	return 0;
+}
+
+#endif /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */
+
 /**
  * phy_probe - probe and init a PHY device
  * @dev: device to probe and init
@@ -2922,6 +3019,12 @@ static int phy_probe(struct device *dev)
 
 	mutex_unlock(&phydev->lock);
 
+	/* LEDs have to be registered with phydev mutex unlocked, because some operations can be
+	 * called during registration that lock the mutex themselves
+	 */
+	if (!err)
+		of_phy_probe_leds(phydev);
+
 	return err;
 }
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 52881e21ad951..8a4ab72c74dd4 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -14,6 +14,7 @@
 #include <linux/compiler.h>
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
+#include <linux/leds-hw-controlled.h>
 #include <linux/linkmode.h>
 #include <linux/netlink.h>
 #include <linux/mdio.h>
@@ -741,6 +742,9 @@ struct phy_driver {
 	int (*set_loopback)(struct phy_device *dev, bool enable);
 	int (*get_sqi)(struct phy_device *dev);
 	int (*get_sqi_max)(struct phy_device *dev);
+
+	/* PHY connected and controlled LEDs */
+	const struct hw_controlled_led_ops *led_ops;
 };
 #define to_phy_driver(d) container_of(to_mdio_common_driver(d),		\
 				      struct phy_driver, mdiodrv)
-- 
2.26.2

Powered by blists - more mailing lists